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/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 }} 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 }} 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/Android.mk b/Android.mk index 3779eb53416ba..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) \ @@ -41,6 +44,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) \ diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fe68734439bc..1cce17fc0330c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -231,29 +231,43 @@ if(SDL_SHARED_DEFAULT AND SDL_STATIC_DEFAULT AND SDL_SHARED_AVAILABLE) endif() endif() -set(SDL_SUBSYSTEMS - Audio - Video - Render - Joystick - Haptic - Hidapi - Power - Sensor -) -foreach(_SUB IN LISTS SDL_SUBSYSTEMS) - string(TOUPPER ${_SUB} _OPT) - if(NOT DEFINED SDL_${_OPT}_DEFAULT) - set(SDL_${_OPT}_DEFAULT ON) +set(SDL_SUBSYSTEMS ) + +macro(define_sdl_subsystem _name) + cmake_parse_arguments("_ds" "" "" "DEPS" ${ARGN}) + string(TOUPPER ${_name} _uname) + if(NOT DEFINED SDL_${_uname}_DEFAULT) + set(SDL_${_uname}_DEFAULT ON) endif() - option(SDL_${_OPT} "Enable the ${_SUB} subsystem" ${SDL_${_OPT}_DEFAULT}) -endforeach() + if(_ds_DEPS) + cmake_dependent_option(SDL_${_uname} "Enable the ${_name} subsystem" "${SDL_${_uname}_DEFAULT}" "${_ds_DEPS}" OFF) + else() + option(SDL_${_uname} "Enable the ${_name} subsystem" "${SDL_${_uname}_DEFAULT}") + endif() + list(APPEND SDL_SUBSYSTEMS "${_name}") +endmacro() + +define_sdl_subsystem(Audio) +define_sdl_subsystem(Video) +define_sdl_subsystem(Render DEPS SDL_VIDEO) +define_sdl_subsystem(Camera DEPS SDL_VIDEO) +define_sdl_subsystem(Joystick) +define_sdl_subsystem(Haptic) +define_sdl_subsystem(Hidapi) +define_sdl_subsystem(Power) +define_sdl_subsystem(Sensor) cmake_dependent_option(SDL_FRAMEWORK "Build SDL libraries as Apple Framework" OFF "APPLE" OFF) 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) @@ -284,54 +298,55 @@ set_option(SDL_LIBC "Use the system C library" ${SDL_LIBC_DEFAULT set_option(SDL_SYSTEM_ICONV "Use iconv() from system-installed libraries" ${SDL_SYSTEM_ICONV_DEFAULT}) set_option(SDL_LIBICONV "Prefer iconv() from libiconv, if available, over libc version" OFF) set_option(SDL_GCC_ATOMICS "Use gcc builtin atomics" ${SDL_GCC_ATOMICS_DEFAULT}) -dep_option(SDL_DBUS "Enable D-Bus support" ON ${UNIX_SYS} OFF) -set_option(SDL_DISKAUDIO "Support the disk writer audio driver" ON) -set_option(SDL_DUMMYAUDIO "Support the dummy audio driver" ON) -set_option(SDL_DUMMYVIDEO "Use dummy video driver" ON) -dep_option(SDL_IBUS "Enable IBus support" ON ${UNIX_SYS} OFF) -dep_option(SDL_OPENGL "Include OpenGL support" ON "NOT VISIONOS" OFF) -dep_option(SDL_OPENGLES "Include OpenGL ES support" ON "NOT VISIONOS" OFF) +dep_option(SDL_DBUS "Enable D-Bus support" ON "${UNIX_SYS}" OFF) +dep_option(SDL_DISKAUDIO "Support the disk writer audio driver" ON "SDL_AUDIO" OFF) +dep_option(SDL_DUMMYAUDIO "Support the dummy audio driver" ON "SDL_AUDIO" OFF) +dep_option(SDL_DUMMYVIDEO "Use dummy video driver" ON "SDL_VIDEO" OFF) +dep_option(SDL_IBUS "Enable IBus support" ON "${UNIX_SYS}" OFF) +dep_option(SDL_OPENGL "Include OpenGL support" ON "SDL_VIDEO;NOT VISIONOS" OFF) +dep_option(SDL_OPENGLES "Include OpenGL ES support" ON "SDL_VIDEO;NOT VISIONOS" OFF) set_option(SDL_PTHREADS "Use POSIX threads for multi-threading" ${SDL_PTHREADS_DEFAULT}) dep_option(SDL_PTHREADS_SEM "Use pthread semaphores" ON "SDL_PTHREADS" OFF) -dep_option(SDL_OSS "Support the OSS audio API" ${SDL_OSS_DEFAULT} "UNIX_SYS OR RISCOS" OFF) -set_option(SDL_ALSA "Support the ALSA audio API" ${UNIX_SYS}) +dep_option(SDL_OSS "Support the OSS audio API" ${SDL_OSS_DEFAULT} "UNIX_SYS OR RISCOS;SDL_AUDIO" OFF) +dep_option(SDL_ALSA "Support the ALSA audio API" ${UNIX_SYS} "SDL_AUDIO" OFF) dep_option(SDL_ALSA_SHARED "Dynamically load ALSA audio support" ON "SDL_ALSA" OFF) -set_option(SDL_JACK "Support the JACK audio API" ${UNIX_SYS}) +dep_option(SDL_JACK "Support the JACK audio API" ${UNIX_SYS} "SDL_AUDIO" OFF) dep_option(SDL_JACK_SHARED "Dynamically load JACK audio support" ON "SDL_JACK" OFF) set_option(SDL_PIPEWIRE "Use Pipewire audio" ${UNIX_SYS}) dep_option(SDL_PIPEWIRE_SHARED "Dynamically load Pipewire support" ON "SDL_PIPEWIRE" OFF) -set_option(SDL_PULSEAUDIO "Use PulseAudio" ${UNIX_SYS}) +dep_option(SDL_PULSEAUDIO "Use PulseAudio" ${UNIX_SYS} "SDL_AUDIO" OFF) dep_option(SDL_PULSEAUDIO_SHARED "Dynamically load PulseAudio support" ON "SDL_PULSEAUDIO" OFF) -set_option(SDL_SNDIO "Support the sndio audio API" ${UNIX_SYS}) +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}) -set_option(SDL_X11 "Use X11 video driver" ${UNIX_SYS}) +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}) string(TOUPPER "SDL_X11_${_SUB}" _OPT) dep_option(${_OPT} "Enable ${_SUB} support" ON "SDL_X11" OFF) endforeach() -set_option(SDL_WAYLAND "Use Wayland video driver" ${UNIX_SYS}) +dep_option(SDL_WAYLAND "Use Wayland video driver" ${UNIX_SYS} "SDL_VIDEO" OFF) dep_option(SDL_WAYLAND_SHARED "Dynamically load Wayland support" ON "SDL_WAYLAND" OFF) dep_option(SDL_WAYLAND_LIBDECOR "Use client-side window decorations on Wayland" ON "SDL_WAYLAND" OFF) dep_option(SDL_WAYLAND_LIBDECOR_SHARED "Dynamically load libdecor support" ON "SDL_WAYLAND_LIBDECOR;SDL_WAYLAND_SHARED" OFF) -dep_option(SDL_RPI "Use Raspberry Pi video driver" ON "UNIX_SYS;SDL_CPU_ARM32 OR SDL_CPU_ARM64" OFF) -dep_option(SDL_ROCKCHIP "Use ROCKCHIP Hardware Acceleration video driver" ON "UNIX_SYS;SDL_CPU_ARM32 OR SDL_CPU_ARM64" OFF) -set_option(SDL_COCOA "Use Cocoa video driver" ${APPLE}) -set_option(SDL_DIRECTX "Use DirectX for Windows audio/video" ${WINDOWS}) -set_option(SDL_XINPUT "Use Xinput for Windows" ${WINDOWS}) -set_option(SDL_WASAPI "Use the Windows WASAPI audio driver" ${WINDOWS}) -set_option(SDL_RENDER_D3D "Enable the Direct3D render driver" ${WINDOWS}) -set_option(SDL_RENDER_METAL "Enable the Metal render driver" ${APPLE}) +dep_option(SDL_RPI "Use Raspberry Pi video driver" ON "SDL_VIDEO;UNIX_SYS;SDL_CPU_ARM32 OR SDL_CPU_ARM64" OFF) +dep_option(SDL_ROCKCHIP "Use ROCKCHIP Hardware Acceleration video driver" ON "SDL_VIDEO;UNIX_SYS;SDL_CPU_ARM32 OR SDL_CPU_ARM64" OFF) +dep_option(SDL_COCOA "Use Cocoa video driver" ON "APPLE" OFF) +dep_option(SDL_DIRECTX "Use DirectX for Windows audio/video" ON "SDL_AUDIO OR SDL_VIDEO;WINDOWS" OFF) +dep_option(SDL_XINPUT "Use Xinput for Windows" ON "WINDOWS" OFF) +dep_option(SDL_WASAPI "Use the Windows WASAPI audio driver" ON "WINDOWS;SDL_AUDIO" OFF) +dep_option(SDL_RENDER_D3D "Enable the Direct3D render driver" ON "SDL_RENDER" OFF) +dep_option(SDL_RENDER_METAL "Enable the Metal render driver" ON "SDL_RENDER;${APPLE}" OFF) dep_option(SDL_VIVANTE "Use Vivante EGL video driver" ON "${UNIX_SYS};SDL_CPU_ARM32" OFF) -dep_option(SDL_VULKAN "Enable Vulkan support" ON "ANDROID OR APPLE OR LINUX OR WINDOWS" OFF) -set_option(SDL_METAL "Enable Metal support" ${APPLE}) -set_option(SDL_KMSDRM "Use KMS DRM video driver" ${UNIX_SYS}) +dep_option(SDL_VULKAN "Enable Vulkan support" ON "SDL_VIDEO;ANDROID OR APPLE OR LINUX OR WINDOWS" OFF) +dep_option(SDL_RENDER_VULKAN "Enable the Vulkan render driver" ON "SDL_RENDER;SDL_VULKAN;ANDROID OR APPLE OR LINUX OR WINDOWS" OFF) +dep_option(SDL_METAL "Enable Metal support" ON "APPLE" OFF) +dep_option(SDL_KMSDRM "Use KMS DRM video driver" ${UNIX_SYS} "SDL_VIDEO" OFF) 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_DUMMYCAMERA "Support the dummy camera driver" ON SDL_CAMERA 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) @@ -371,14 +386,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) @@ -453,6 +476,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" @@ -1034,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 @@ -1043,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 @@ -1171,6 +1194,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) @@ -1288,6 +1328,12 @@ if(ANDROID) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/sensor/android/*.c") endif() + if(SDL_CAMERA) + set(SDL_CAMERA_DRIVER_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") @@ -1398,6 +1444,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") @@ -1497,6 +1549,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 @@ -1520,6 +1575,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_DRIVER_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() @@ -1707,10 +1768,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) @@ -1952,6 +2014,10 @@ elseif(WINDOWS) if(SDL_VULKAN) set(SDL_VIDEO_VULKAN 1) set(HAVE_VULKAN TRUE) + if(SDL_RENDER_VULKAN) + set(SDL_VIDEO_RENDER_VULKAN 1) + set(HAVE_RENDER_VULKAN TRUE) + endif() endif() endif() @@ -1989,6 +2055,14 @@ elseif(WINDOWS) endif() endif() + if(SDL_CAMERA) + if(NOT WINDOWS_STORE) + set(HAVE_CAMERA TRUE) + set(SDL_CAMERA_DRIVER_MEDIAFOUNDATION 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/mediafoundation/*.c") + endif() + endif() + sdl_glob_sources(SHARED "${SDL3_SOURCE_DIR}/src/core/windows/*.rc") if(MINGW OR CYGWIN) sdl_pc_link_options("-mwindows") @@ -2018,8 +2092,12 @@ 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") + if(SDL_CAMERA) + if(IOS OR TVOS OR MACOSX OR DARWIN) + set(SDL_CAMERA_DRIVER_COREMEDIA 1) + set(HAVE_CAMERA TRUE) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/coremedia/*.m") + endif() endif() if(IOS OR TVOS OR VISIONOS) @@ -2181,6 +2259,10 @@ elseif(APPLE) if(SDL_VULKAN) set(SDL_VIDEO_VULKAN 1) set(HAVE_VULKAN TRUE) + if(SDL_RENDER_VULKAN) + set(SDL_VIDEO_RENDER_VULKAN 1) + set(HAVE_RENDER_VULKAN TRUE) + endif() endif() if(SDL_METAL) set(SDL_VIDEO_METAL 1) @@ -2271,6 +2353,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) @@ -2720,6 +2807,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_DRIVER_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/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj index e070c31ef76e7..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) @@ -314,6 +314,7 @@ + @@ -398,6 +399,8 @@ + + @@ -498,6 +501,8 @@ $(IntDir)$(TargetName)_cpp.pch $(IntDir)$(TargetName)_cpp.pch + + @@ -576,9 +581,7 @@ - - @@ -647,6 +650,7 @@ + @@ -810,7 +814,6 @@ - diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters index f5d648fe70cb1..c656f29be8e73 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj.filters +++ b/VisualC-GDK/SDL/SDL.vcxproj.filters @@ -52,6 +52,7 @@ + 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/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 45415212dcfd0..f011b1f643f7f 100644 --- a/VisualC/SDL/SDL.vcxproj +++ b/VisualC/SDL/SDL.vcxproj @@ -236,6 +236,7 @@ + @@ -320,6 +321,8 @@ + + @@ -385,6 +388,7 @@ + @@ -393,8 +397,13 @@ Create Create + + + + + @@ -520,6 +529,7 @@ + @@ -657,7 +667,6 @@ - @@ -684,4 +693,4 @@ - + \ No newline at end of file diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters index 8073004813fd2..4f292f1bfc8e1 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} @@ -175,11 +178,26 @@ {0000ddc7911820dbe64274d3654f0000} + + {0000de1b75e1a954834693f1c81e0000} + + + {0000fc2700d453b3c8d79fe81e1c0000} + + + {0000fbfe2d21e4f451142e7d0e870000} + + + {5115ba31-20f8-4eab-a8c5-6a572ab78ff7} + API Headers + + API Headers + API Headers @@ -393,6 +411,12 @@ API Headers + + camera + + + camera + main @@ -832,9 +856,21 @@ + + render\vulkan + + + camera\dummy + + + camera\mediafoundation + + + camera + main\generic @@ -1063,6 +1099,9 @@ joystick\dummy + + joystick\gdk + joystick\hidapi @@ -1189,9 +1228,6 @@ video - - video - video @@ -1404,8 +1440,17 @@ + + render\vulkan + + + render\vulkan + - + + + + \ No newline at end of file diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj index e7925013650cb..759c4b5963421 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, ); }; @@ -394,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 */; }; @@ -421,14 +425,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 +526,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 +950,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 +1053,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 +1092,14 @@ path = ios; sourceTree = ""; }; + 0000DBB4B95F4CC5CAE80000 /* coremedia */ = { + isa = PBXGroup; + children = ( + 00008B79BF08CBCEAC460000 /* SDL_camera_coremedia.m */, + ); + path = coremedia; + sourceTree = ""; + }; 0153844A006D81B07F000001 /* Public Headers */ = { isa = PBXGroup; children = ( @@ -1083,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 */, @@ -1141,7 +1168,6 @@ 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 */, @@ -1182,6 +1208,7 @@ children = ( A7D8A57223E2513D00DCD162 /* atomic */, A7D8A86423E2513F00DCD162 /* audio */, + 00002EC7DF7A0A31B32A0000 /* camera */, F36C7ACF294B9F5E004D61C3 /* core */, A7D8A77423E2513E00DCD162 /* cpuinfo */, A7D8A5D723E2513D00DCD162 /* dynapi */, @@ -1462,12 +1489,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,11 +2189,12 @@ 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 */, + 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 */, @@ -2182,11 +2205,12 @@ 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 */, - F3DDCC5C2AFD42B600B0842B /* SDL_sysvideocapture.h in Headers */, A7D8AF1223E2514100DCD162 /* SDL_cocoaevents.h in Headers */, A7D8AE8E23E2514100DCD162 /* SDL_cocoakeyboard.h in Headers */, A7D8AF0623E2514100DCD162 /* SDL_cocoamessagebox.h in Headers */, @@ -2195,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 */, @@ -2215,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 */, @@ -2228,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 */, @@ -2235,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 */, @@ -2243,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 */, @@ -2260,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 */, @@ -2276,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 */, @@ -2308,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 */, @@ -2333,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 */, @@ -2349,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 */, @@ -2363,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 */, @@ -2382,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 */, @@ -2396,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; }; @@ -2549,7 +2569,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 +2585,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 +2620,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 +2753,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/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/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake index c91df3f395e22..7fae2524b4e17 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) @@ -714,6 +725,10 @@ macro(CheckVulkan) if(SDL_VULKAN) set(SDL_VIDEO_VULKAN 1) set(HAVE_VULKAN TRUE) + if(SDL_RENDER_VULKAN) + set(SDL_VIDEO_RENDER_VULKAN 1) + set(HAVE_RENDER_VULKAN TRUE) + endif() endif() endmacro() diff --git a/docs/README-cmake.md b/docs/README-cmake.md index 337a41ab089ad..73f80f21c3b9a 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,9 +337,15 @@ file(WRITE main.c [===========================================[ /* START of source modifications */ #include +/* + * SDL3/SDL_main.h is explicitly not included such that a terminal window would appear on Windows. + */ 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 +382,7 @@ int main(int argc, char *argv[]) { SDL_DestroyWindow(window); SDL_Quit(); + return 0; } /* END of source modifications */ 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.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_camera.h b/include/SDL3/SDL_camera.h new file mode 100644 index 0000000000000..1b81edad94d16 --- /dev/null +++ b/include/SDL3/SDL_camera.h @@ -0,0 +1,478 @@ +/* + 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. +*/ + +/** + * \file SDL_camera.h + * + * Video Capture for the SDL library. + */ + +#ifndef SDL_camera_h_ +#define SDL_camera_h_ + +#include "SDL3/SDL_video.h" + +#include +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/** + * 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_GetCameraDevices + */ +typedef Uint32 SDL_CameraDeviceID; + + +/** + * The structure used to identify an opened SDL camera + */ +struct SDL_Camera; +typedef struct SDL_Camera SDL_Camera; + +/** + * SDL_CameraSpec structure + * + * \sa SDL_GetCameraDeviceSupportedFormats + * \sa SDL_GetCameraFormat + * + */ +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, (num / dom) == duration) */ + 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. + * + * 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. + * + * \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 + */ +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 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`). + * + * 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 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_GetCameraDevices + * \sa SDL_OpenCameraDevice + */ +extern DECLSPEC SDL_CameraSpec *SDLCALL SDL_GetCameraDeviceSupportedFormats(SDL_CameraDeviceID devid, int *count); + +/** + * Get human-readable device name for a camera. + * + * The returned string is owned by the caller; please release it with + * 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. + * + * \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 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"). + * + * 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. + * + * 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(). + * + * 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 + * 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 + * information. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_GetCameraDevices + * \sa SDL_GetCameraFormat + */ +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. + * + * \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. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_OpenCameraDevice + */ +extern DECLSPEC SDL_CameraDeviceID SDLCALL SDL_GetCameraInstanceID(SDL_Camera *camera); + +/** + * Get the properties associated with an opened camera. + * + * \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. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_GetProperty + * \sa SDL_SetProperty + */ +extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetCameraProperties(SDL_Camera *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. + * + * 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 + * 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_OpenCameraDevice + */ +extern DECLSPEC int SDLCALL SDL_GetCameraFormat(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 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. + * + * 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! + * + * 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. + * + * \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 SDL_Surface * SDLCALL SDL_AcquireCameraFrame(SDL_Camera *camera, Uint64 *timestampNS); + +/** + * Release a frame of video acquired from a camera. + * + * Let the back-end re-use the internal buffer for 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. + * + * If the app needs to keep the surface for a significant time, they should + * make a copy of it and release the original. + * + * The app should not use the surface again after calling this function; + * assume the surface is freed and the pointer is invalid. + * + * \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. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_AcquireCameraFrame + */ +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. + * + * \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. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_OpenCameraWithSpec + * \sa SDL_OpenCamera + */ +extern DECLSPEC void SDLCALL SDL_CloseCamera(SDL_Camera *camera); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include + +#endif /* SDL_camera_h_ */ diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h index d209801ede518..80709f9ec71f6 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++ */ @@ -205,6 +206,12 @@ 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. */ + 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 */ SDL_EVENT_RENDER_DEVICE_RESET, /**< The device has been reset and all textures need to be recreated */ @@ -526,6 +533,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, ::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; + Uint8 padding2; + Uint8 padding3; +} SDL_CameraDeviceEvent; /** * Touch finger event structure (event.tfinger.*) @@ -552,18 +571,16 @@ 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; @@ -572,18 +589,16 @@ 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; @@ -592,18 +607,16 @@ 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; @@ -699,6 +712,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/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 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. diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h index acbbd84ea223c..f67abaf9b660d 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 * @@ -1720,6 +1736,18 @@ extern "C" { */ #define SDL_HINT_RENDER_DIRECT3D11_DEBUG "SDL_RENDER_DIRECT3D11_DEBUG" +/** + * A variable controlling whether to enable Vulkan Validation Layers + * + * + * This variable can be set to the following values: + * "0" - Disable Validation Layer use + * "1" - Enable Validation Layer use + * + * By default, SDL does not use Vulkan Validation Layers. + */ +#define SDL_HINT_RENDER_VULKAN_DEBUG "SDL_RENDER_VULKAN_DEBUG" + /** * A variable specifying which render driver to use. * @@ -1733,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. @@ -2478,7 +2507,6 @@ extern "C" { */ #define SDL_HINT_XINPUT_ENABLED "SDL_XINPUT_ENABLED" - /** * An enumeration of hint priorities */ @@ -2634,23 +2662,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/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/SDL3/SDL_pen.h b/include/SDL3/SDL_pen.h index 36c67b7cf7d29..dd1b1970f614e 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 */ @@ -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 */ diff --git a/include/SDL3/SDL_pixels.h b/include/SDL3/SDL_pixels.h index e2f238bb1be19..d3def2c41bf08 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; @@ -474,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; @@ -495,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, @@ -511,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; @@ -521,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; @@ -561,10 +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_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_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 { @@ -576,16 +574,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 +592,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_properties.h b/include/SDL3/SDL_properties.h index 80a01cc123e01..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 @@ -183,6 +185,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 @@ -248,6 +253,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 +279,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 +307,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 +326,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 +348,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 +370,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 +392,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 +408,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/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++ */ diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index 8d80beee199ef..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 */ @@ -242,14 +242,29 @@ 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 - * colorspace, but values can go beyond 1.0 and float (linear) format - * textures can be used for HDR content. + * 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 * 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 +278,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. @@ -342,15 +363,54 @@ 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. 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. + * - `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. + * + * 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 + * - `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 * SDL_GetError() for more information. @@ -362,14 +422,24 @@ 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_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" +#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. @@ -468,8 +538,8 @@ 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 - * floating point textures, SDL_COLORSPACE_HDR10 for 10-bit textures, + * 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. * - `SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER`: one of the enumerated values in @@ -480,6 +550,17 @@ 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: * @@ -539,6 +620,12 @@ 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 @@ -560,6 +647,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" @@ -576,7 +665,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. @@ -585,6 +674,17 @@ 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: * @@ -604,6 +704,17 @@ extern DECLSPEC SDL_Texture *SDLCALL SDL_CreateTextureWithProperties(SDL_Rendere * - `SDL_PROP_TEXTURE_D3D12_TEXTURE_V_POINTER`: the ID3D12Resource associated * with the V plane of a YUV texture * + * With the vulkan renderer: + * + * - `SDL_PROP_TEXTURE_VULKAN_TEXTURE_POINTER`: the VkImage associated with + * the texture + * - `SDL_PROP_TEXTURE_VULKAN_TEXTURE_U_POINTER`: the VkImage associated with + * the U plane of a YUV texture + * - `SDL_PROP_TEXTURE_VULKAN_TEXTURE_V_POINTER`: the VkImage associated with + * the V plane of a YUV texture + * - `SDL_PROP_TEXTURE_VULKAN_TEXTURE_UV_POINTER`: the VkImage associated with + * the UV plane of a NV12/NV21 texture + * * With the opengl renderer: * * - `SDL_PROP_TEXTURE_OPENGL_TEXTURE_NUMBER`: the GLuint texture associated @@ -634,6 +745,11 @@ 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. @@ -646,6 +762,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" @@ -664,6 +782,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_NUMBER "SDL.texture.vulkan.texture" /** * Get the renderer that created an SDL_Texture. @@ -1989,6 +2108,34 @@ 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/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/SDL3/SDL_surface.h b/include/SDL3/SDL_surface.h index 8e064109a9b1e..abff60facdb67 100644 --- a/include/SDL3/SDL_surface.h +++ b/include/SDL3/SDL_surface.h @@ -206,14 +206,24 @@ 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 - * point formats, SDL_COLORSPACE_HDR10 for 10-bit formats, + * 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. + * for YUV 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 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. * - `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. + * 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 @@ -227,6 +237,8 @@ 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_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,9 +259,9 @@ 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, - * SDL_COLORSPACE_HDR10 for 10-bit formats, SDL_COLORSPACE_SRGB for other RGB - * surfaces and SDL_COLORSPACE_BT709_FULL for YUV textures. + * 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. * * \param surface the SDL_Surface structure to query * \param colorspace a pointer filled in with an SDL_ColorSpace value @@ -714,6 +726,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. * @@ -723,7 +736,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. @@ -752,11 +765,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 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 @@ -764,7 +781,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 e3b995583170e..7482e255475ae 100644 --- a/include/SDL3/SDL_video.h +++ b/include/SDL3/SDL_video.h @@ -355,11 +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 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_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. + * - `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. * * \param displayID the instance ID of the display to query * \returns a valid property ID on success or 0 on failure; call @@ -373,7 +380,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_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/include/SDL3/SDL_video_capture.h b/include/SDL3/SDL_video_capture.h deleted file mode 100644 index 80a21605460a8..0000000000000 --- a/include/SDL3/SDL_video_capture.h +++ /dev/null @@ -1,377 +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. -*/ - -/** - * \file SDL_video_capture.h - * - * Video Capture for the SDL library. - */ - -#ifndef SDL_video_capture_h_ -#define SDL_video_capture_h_ - -#include "SDL3/SDL_video.h" - -#include -/* Set up for C function definitions, even when using C++ */ -#ifdef __cplusplus -extern "C" { -#endif - -/** - * This is a unique ID for a video capture 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 - */ -typedef Uint32 SDL_VideoCaptureDeviceID; - - -/** - * The structure used to identify an SDL video capture device - */ -struct SDL_VideoCaptureDevice; -typedef struct SDL_VideoCaptureDevice SDL_VideoCaptureDevice; - -#define SDL_VIDEO_CAPTURE_ALLOW_ANY_CHANGE 1 - -/** - * SDL_VideoCaptureSpec structure - * - * Only those field can be 'desired' when configuring the device: - * - format - * - width - * - height - * - * \sa SDL_GetVideoCaptureFormat - * \sa SDL_GetVideoCaptureFrameSize - * - */ -typedef struct SDL_VideoCaptureSpec -{ - Uint32 format; /**< Frame SDL_PixelFormatEnum format */ - int width; /**< Frame width */ - int height; /**< Frame height */ -} SDL_VideoCaptureSpec; - -/** - * SDL Video Capture Status - * - * Change states but calling the function in this order: - * - * SDL_OpenVideoCapture() - * SDL_SetVideoCaptureSpec() -> Init - * SDL_StartVideoCapture() -> Playing - * SDL_StopVideoCapture() -> Stopped - * SDL_CloseVideoCapture() - * - */ -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 Video Capture Status - */ -typedef struct SDL_VideoCaptureFrame -{ - 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; - - -/** - * Get a list of currently connected video capture 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 - * 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 - */ -extern DECLSPEC SDL_VideoCaptureDeviceID *SDLCALL SDL_GetVideoCaptureDevices(int *count); - -/** - * Open a Video Capture device - * - * \param instance_id the video capture 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 - */ -extern DECLSPEC SDL_VideoCaptureDevice *SDLCALL SDL_OpenVideoCapture(SDL_VideoCaptureDeviceID instance_id); - -/** - * Set specification - * - * \param device opened video capture device - * \param desired desired video capture spec - * \param obtained obtained video capture 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 - */ -extern DECLSPEC int SDLCALL SDL_SetVideoCaptureSpec(SDL_VideoCaptureDevice *device, - const SDL_VideoCaptureSpec *desired, - SDL_VideoCaptureSpec *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 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 - */ -extern DECLSPEC SDL_VideoCaptureDevice *SDLCALL SDL_OpenVideoCaptureWithSpec(SDL_VideoCaptureDeviceID instance_id, - const SDL_VideoCaptureSpec *desired, - SDL_VideoCaptureSpec *obtained, - int allowed_changes); - -/** - * Get device name - * - * \param instance_id the video capture device instance ID - * \returns device name, shouldn't be freed - * - * \since This function is available since SDL 3.0.0. - * - * \sa SDL_GetVideoCaptureDevices - */ -extern DECLSPEC const char * SDLCALL SDL_GetVideoCaptureDeviceName(SDL_VideoCaptureDeviceID instance_id); - -/** - * Get the obtained video capture spec - * - * \param device opened video capture device - * \param spec The SDL_VideoCaptureSpec 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 - */ -extern DECLSPEC int SDLCALL SDL_GetVideoCaptureSpec(SDL_VideoCaptureDevice *device, SDL_VideoCaptureSpec *spec); - - -/** - * Get frame format of video capture device. - * - * The value can be used to fill SDL_VideoCaptureSpec structure. - * - * \param device opened video capture 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. - * - * \since This function is available since SDL 3.0.0. - * - * \sa SDL_GetNumVideoCaptureFormats - */ -extern DECLSPEC int SDLCALL SDL_GetVideoCaptureFormat(SDL_VideoCaptureDevice *device, - int index, - Uint32 *format); - -/** - * Number of available formats for the device - * - * \param device opened video capture 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 - */ -extern DECLSPEC int SDLCALL SDL_GetNumVideoCaptureFormats(SDL_VideoCaptureDevice *device); - -/** - * Get frame sizes of the device and the specified input format. - * - * The value can be used to fill SDL_VideoCaptureSpec structure. - * - * \param device opened video capture 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. - * - * \since This function is available since SDL 3.0.0. - * - * \sa SDL_GetNumVideoCaptureFrameSizes - */ -extern DECLSPEC int SDLCALL SDL_GetVideoCaptureFrameSize(SDL_VideoCaptureDevice *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 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 - */ -extern DECLSPEC int SDLCALL SDL_GetNumVideoCaptureFrameSizes(SDL_VideoCaptureDevice *device, Uint32 format); - - -/** - * Get video capture status - * - * \param device opened video capture 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 - */ -extern DECLSPEC SDL_VideoCaptureStatus SDLCALL SDL_GetVideoCaptureStatus(SDL_VideoCaptureDevice *device); - -/** - * Start video capture - * - * \param device opened video capture 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 - */ -extern DECLSPEC int SDLCALL SDL_StartVideoCapture(SDL_VideoCaptureDevice *device); - -/** - * Acquire a frame. - * - * The frame is a memory pointer to the image data, whose size and format are - * given by the the obtained spec. - * - * 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 used, the frame should be released with SDL_ReleaseVideoCaptureFrame - * - * \param device opened video capture 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 - */ -extern DECLSPEC int SDLCALL SDL_AcquireVideoCaptureFrame(SDL_VideoCaptureDevice *device, SDL_VideoCaptureFrame *frame); - -/** - * Release a frame. - * - * Let the back-end re-use the internal buffer for video capture. - * - * All acquired frames should be released before closing the device. - * - * \param device opened video capture 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 - */ -extern DECLSPEC int SDLCALL SDL_ReleaseVideoCaptureFrame(SDL_VideoCaptureDevice *device, SDL_VideoCaptureFrame *frame); - -/** - * Stop Video Capture - * - * \param device opened video capture 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 - */ -extern DECLSPEC int SDLCALL SDL_StopVideoCapture(SDL_VideoCaptureDevice *device); - -/** - * Use this function to shut down video_capture processing and close the - * video_capture device. - * - * \param device opened video capture device - * - * \since This function is available since SDL 3.0.0. - * - * \sa SDL_OpenVideoCaptureWithSpec - * \sa SDL_OpenVideoCapture - */ -extern DECLSPEC void SDLCALL SDL_CloseVideoCapture(SDL_VideoCaptureDevice *device); - -/* Ends C function definitions when using C++ */ -#ifdef __cplusplus -} -#endif -#include - -#endif /* SDL_video_capture_h_ */ diff --git a/include/SDL3/SDL_vulkan.h b/include/SDL3/SDL_vulkan.h index 98590786e56fc..64544c8b699cd 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,15 +149,14 @@ 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 - * 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(Uint32 *pCount); +extern DECLSPEC char const* const* SDLCALL SDL_Vulkan_GetInstanceExtensions(Uint32 *count); /** * Create a Vulkan rendering surface for a window. diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake index 2b4df48157d52..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 @@ -246,15 +244,11 @@ #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@ #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 +259,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@ @@ -415,6 +410,7 @@ #cmakedefine SDL_VIDEO_RENDER_D3D11 @SDL_VIDEO_RENDER_D3D11@ #cmakedefine SDL_VIDEO_RENDER_D3D12 @SDL_VIDEO_RENDER_D3D12@ #cmakedefine SDL_VIDEO_RENDER_METAL @SDL_VIDEO_RENDER_METAL@ +#cmakedefine SDL_VIDEO_RENDER_VULKAN @SDL_VIDEO_RENDER_VULKAN@ #cmakedefine SDL_VIDEO_RENDER_OGL @SDL_VIDEO_RENDER_OGL@ #cmakedefine SDL_VIDEO_RENDER_OGL_ES2 @SDL_VIDEO_RENDER_OGL_ES2@ #cmakedefine SDL_VIDEO_RENDER_PS2 @SDL_VIDEO_RENDER_PS2@ @@ -467,6 +463,15 @@ #cmakedefine SDL_FILESYSTEM_PS2 @SDL_FILESYSTEM_PS2@ #cmakedefine SDL_FILESYSTEM_N3DS @SDL_FILESYSTEM_N3DS@ +/* Enable camera subsystem */ +#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@ +#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@ @@ -492,7 +497,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/include/build_config/SDL_build_config_android.h b/include/build_config/SDL_build_config_android.h index ad75cc1fbabc4..64f8076e00b01 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 @@ -62,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 @@ -189,4 +188,8 @@ /* Enable the filesystem driver */ #define SDL_FILESYSTEM_ANDROID 1 +/* 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/include/build_config/SDL_build_config_emscripten.h b/include/build_config/SDL_build_config_emscripten.h index 7fb5294ded8d9..89d5531f30ac6 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 @@ -64,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 @@ -208,4 +207,7 @@ /* Enable system filesystem support */ #define SDL_FILESYSTEM_EMSCRIPTEN 1 +/* Enable the camera driver */ +#define SDL_CAMERA_DRIVER_EMSCRIPTEN 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 e79ca4ce20585..e130cc4673053 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 @@ -54,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 @@ -197,8 +196,6 @@ #define SDL_VIDEO_METAL 1 #endif -#define HAVE_COREMEDIA 1 - /* Enable system power support */ #define SDL_POWER_UIKIT 1 @@ -211,4 +208,11 @@ /* enable filesystem support */ #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 66bdb5ea42808..39ee50d4563a4 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 @@ -58,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 @@ -260,14 +259,16 @@ #endif #endif -#define HAVE_COREMEDIA 1 - /* Enable system power support */ #define SDL_POWER_MACOSX 1 /* enable filesystem support */ #define SDL_FILESYSTEM_COCOA 1 +/* enable camera support */ +#define SDL_CAMERA_DRIVER_COREMEDIA 1 +#define SDL_CAMERA_DRIVER_DUMMY 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..06d02557ea223 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_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 17872ef065818..3449627b02e8a 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_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 d2846abc8e9bc..19873dbf17842 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 @@ -132,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 @@ -235,6 +236,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 @@ -301,10 +303,18 @@ 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 /* Enable filesystem support */ #define SDL_FILESYSTEM_WINDOWS 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/include/build_config/SDL_build_config_wingdk.h b/include/build_config/SDL_build_config_wingdk.h index 3730221d919fa..992052a819125 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,12 +71,11 @@ #define HAVE_WCHAR_H 1 /* C library functions */ +#define HAVE_LIBC 1 #define HAVE_MALLOC 1 #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 @@ -156,11 +153,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) @@ -182,6 +174,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 @@ -243,6 +245,9 @@ /* Enable filesystem support */ #define SDL_FILESYSTEM_WINDOWS 1 +/* Enable the camera driver (src/camera/dummy/\*.c) */ /* !!! FIXME */ +#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 bb014efe93d85..b5e5725fba10f 100644 --- a/include/build_config/SDL_build_config_winrt.h +++ b/include/build_config/SDL_build_config_winrt.h @@ -71,12 +71,11 @@ #define HAVE_WCHAR_H 1 /* C library functions */ +#define HAVE_LIBC 1 #define HAVE_MALLOC 1 #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 @@ -214,4 +213,7 @@ /* Enable system power support */ #define SDL_POWER_WINRT 1 +/* Enable the camera driver (src/camera/dummy/\*.c) */ /* !!! FIXME */ +#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 aecbd3a1222c1..8615036734984 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,12 +71,11 @@ #define HAVE_WCHAR_H 1 /* C library functions */ +#define HAVE_LIBC 1 #define HAVE_MALLOC 1 #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 @@ -156,11 +153,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) @@ -179,7 +171,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 */ @@ -232,4 +234,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_DRIVER_DUMMY 1 + #endif /* SDL_build_config_wingdk_h_ */ diff --git a/src/SDL.c b/src/SDL.c index a3bd6a52ebdd9..579cb67f1ed17 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" @@ -46,6 +47,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 @@ -368,6 +370,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; @@ -385,6 +411,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/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/SDL_internal.h b/src/SDL_internal.h index a8e077583b68d..1494b9a4a6a7a 100644 --- a/src/SDL_internal.h +++ b/src/SDL_internal.h @@ -223,6 +223,9 @@ #ifndef SDL_VIDEO_RENDER_VITA_GXM #define SDL_VIDEO_RENDER_VITA_GXM 0 #endif +#ifndef SDL_VIDEO_RENDER_VULKAN +#define SDL_VIDEO_RENDER_VULKAN 0 +#endif #else /* define all as 0 */ #undef SDL_VIDEO_RENDER_SW #define SDL_VIDEO_RENDER_SW 0 @@ -244,6 +247,8 @@ #define SDL_VIDEO_RENDER_PSP 0 #undef SDL_VIDEO_RENDER_VITA_GXM #define SDL_VIDEO_RENDER_VITA_GXM 0 +#undef SDL_VIDEO_RENDER_VULKAN +#define SDL_VIDEO_RENDER_VULKAN 0 #endif /* SDL_RENDER_DISABLED */ #define SDL_HAS_RENDER_DRIVER \ @@ -256,7 +261,8 @@ SDL_VIDEO_RENDER_OGL_ES2 | \ SDL_VIDEO_RENDER_PS2 | \ SDL_VIDEO_RENDER_PSP | \ - SDL_VIDEO_RENDER_VITA_GXM) + SDL_VIDEO_RENDER_VITA_GXM | \ + SDL_VIDEO_RENDER_VULKAN ) #if !defined(SDL_RENDER_DISABLED) && !SDL_HAS_RENDER_DRIVER #error SDL_RENDER enabled without any backend drivers. 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/audio/SDL_audio.c b/src/audio/SDL_audio.c index 6a6d141a017a3..8b00b47fa91f2 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -1795,15 +1795,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 (stream) { + SDL_UnlockMutex(stream->lock); } break; } diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c new file mode 100644 index 0000000000000..f0e58fc16ae2d --- /dev/null +++ b/src/camera/SDL_camera.c @@ -0,0 +1,1505 @@ +/* + 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_syscamera.h" +#include "SDL_camera_c.h" +#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 + &V4L2_bootstrap, +#endif +#ifdef SDL_CAMERA_DRIVER_COREMEDIA + &COREMEDIA_bootstrap, +#endif +#ifdef SDL_CAMERA_DRIVER_ANDROID + &ANDROIDCAMERA_bootstrap, +#endif +#ifdef SDL_CAMERA_DRIVER_EMSCRIPTEN + &EMSCRIPTENCAMERA_bootstrap, +#endif +#ifdef SDL_CAMERA_DRIVER_MEDIAFOUNDATION + &MEDIAFOUNDATION_bootstrap, +#endif +#ifdef SDL_CAMERA_DRIVER_DUMMY + &DUMMYCAMERA_bootstrap, +#endif + NULL +}; + +static SDL_CameraDriver camera_driver; + + +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; +} + +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); + 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; +} + + +// 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) { + return; + } + + SDL_AtomicSet(&device->shutdown, 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; + } + + // 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) { + device->ReleaseFrame(device, i->surface); + } + for (SurfaceList *i = device->app_held_output_surfaces.next; i != NULL; i = i->next) { + device->ReleaseFrame(device, i->surface); + } + } + + 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); + + 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; + 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. +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); + } +} + + +// 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. + } +} + +void RefPhysicalCameraDevice(SDL_CameraDevice *device) +{ + SDL_AtomicIncRef(&device->refcount); +} + +static void ObtainPhysicalCameraDeviceObj(SDL_CameraDevice *device) SDL_NO_THREAD_SAFETY_ANALYSIS // !!! FIXMEL SDL_ACQUIRE +{ + if (device) { + RefPhysicalCameraDevice(device); + SDL_LockMutex(device->lock); + } +} + +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_SetError("Invalid camera device instance ID"); + } else { + ObtainPhysicalCameraDeviceObj(device); + } + return device; +} + +static void ReleaseCameraDevice(SDL_CameraDevice *device) SDL_NO_THREAD_SAFETY_ANALYSIS // !!! FIXME: SDL_RELEASE +{ + if (device) { + SDL_UnlockMutex(device->lock); + UnrefPhysicalCameraDevice(device); + } +} + +// 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. + +// 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; + } + + // 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. +} + +// The camera backends call this when a new device is plugged in. +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); + SDL_assert((specs != NULL) == (num_specs > 0)); + 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 NULL; + } + + device->name = SDL_strdup(name); + if (!device->name) { + SDL_free(device); + return NULL; + } + + device->position = position; + + device->lock = SDL_CreateMutex(); + if (!device->lock) { + SDL_free(device->name); + SDL_free(device); + return NULL; + } + + 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; + } + + 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--; + } + } + } + + #if DEBUG_CAMERA + 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); + } + #endif + + 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; + } + + // 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 device; +} + +// 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) +{ + if (!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 + // 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_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. 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->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. + p->type = SDL_EVENT_CAMERA_DEVICE_REMOVED; + p->devid = device->instance_id; + p->next = NULL; + pending_tail->next = p; + pending_tail = p; + } + } + + 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); + } + } +} + +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()) { + SDL_SetError("Camera subsystem is not initialized"); + return NULL; + } + + const void *key; + const void *value; + void *iter = NULL; + + 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; + } + } + + SDL_UnlockRWLock(camera_driver.device_hash_lock); + + 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_GetCameraFormat(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. + 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; +} + +char *SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id) +{ + char *retval = NULL; + SDL_CameraDevice *device = ObtainPhysicalCameraDevice(instance_id); + if (device) { + retval = SDL_strdup(device->name); + ReleaseCameraDevice(device); + } + 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; + if (!count) { + count = &dummy_count; + } + + if (!SDL_GetCurrentCameraDriver()) { + *count = 0; + SDL_SetError("Camera subsystem is not initialized"); + return NULL; + } + + SDL_CameraDeviceID *retval = NULL; + + SDL_LockRWLockForReading(camera_driver.device_hash_lock); + int num_devices = SDL_AtomicGet(&camera_driver.device_count); + 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); + + *count = num_devices; + + return retval; +} + +SDL_CameraSpec *SDL_GetCameraDeviceSupportedFormats(SDL_CameraDeviceID instance_id, int *count) +{ + if (count) { + *count = 0; + } + + SDL_CameraDevice *device = ObtainPhysicalCameraDevice(instance_id); + if (!device) { + 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; + } + } + + ReleaseCameraDevice(device); + + return retval; +} + + +// 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 + /* + { + // Set thread priority to THREAD_PRIORITY_VIDEO + extern void Android_JNI_CameraSetThreadPriority(int, int); + Android_JNI_CameraSetThreadPriority(device->iscapture, device); + }*/ +#else + // The camera capture is always a high priority thread + SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); +#endif +} + +SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device) +{ + SDL_LockMutex(device->lock); + + if (SDL_AtomicGet(&device->shutdown)) { + SDL_UnlockMutex(device->lock); + return SDL_FALSE; // we're done, shut it down. + } + + 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; + SDL_Surface *output_surface = NULL; + SurfaceList *slist = NULL; + Uint64 timestampNS = 0; + + // AcquireFrame SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitDevice instead! + const int rc = device->AcquireFrame(device, device->acquire_surface, ×tampNS); + + if (rc == 1) { // new frame acquired! + #if DEBUG_CAMERA + 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--; + device->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!"); + #endif + device->ReleaseFrame(device, device->acquire_surface); + 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; + 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 off the empty 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. + #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; + SDL_SoftStretch(srcsurf, NULL, dstsurf, NULL, SDL_SCALEMODE_NEAREST); // !!! 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, SDL_SCALEMODE_NEAREST); // !!! FIXME: linear scale? letterboxing? + } + + // we made a copy, so we can give the driver back its resources. + device->ReleaseFrame(device, acquired); + } + + // we either released these already after we copied the data, or the pointer was migrated to output_surface. + acquired->pixels = NULL; + acquired->pitch = 0; + + // 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); + } + + return SDL_TRUE; // always go on if not shutting down, even if device failed. +} + +void SDL_CameraThreadShutdown(SDL_CameraDevice *device) +{ + //device->FlushCapture(device); + //camera_driver.impl.ThreadDeinit(device); + //SDL_CameraThreadFinalize(device); +} + +// Actual thread entry point, if driver didn't handle this itself. +static int SDLCALL CameraThread(void *devicep) +{ + SDL_CameraDevice *device = (SDL_CameraDevice *) devicep; + + #if DEBUG_CAMERA + SDL_Log("CAMERA: dev[%p] Start thread 'CameraThread'", devicep); + #endif + + SDL_assert(device != NULL); + SDL_CameraThreadSetup(device); + + do { + 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)); + + SDL_CameraThreadShutdown(device); + + #if DEBUG_CAMERA + SDL_Log("CAMERA: dev[%p] End thread 'CameraThread'", devicep); + #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 (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_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; + } + } + + 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; + + // 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); + 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; + } + } + + SDL_CameraDevice *device = ObtainPhysicalCameraDevice(instance_id); + if (!device) { + return NULL; + } + + if (device->hidden != NULL) { + ReleaseCameraDevice(device); + SDL_SetError("Camera already opened"); // we may remove this limitation at some point. + return NULL; + } + + 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); + + #if DEBUG_CAMERA + 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) { + ClosePhysicalCameraDevice(device); // in case anything is half-initialized. + ReleaseCameraDevice(device); + return NULL; + } + + if (!spec) { + SDL_copyp(&device->spec, &closest); + } else { + SDL_copyp(&device->spec, spec); + } + + SDL_copyp(&device->actual_spec, &closest); + + 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 + } + } + + 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 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); + } + + // 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. + + 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; + + 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; + } + + device->output_surfaces[i].surface = surf; + } + + device->drop_frames = 1; + + // Start the camera thread if necessary + if (!camera_driver.impl.ProvidesOwnCallbackThread) { + char threadname[64]; + SDL_GetCameraThreadName(device, threadname, sizeof (threadname)); + device->thread = SDL_CreateThreadInternal(CameraThread, threadname, 0, device); + if (!device->thread) { + ClosePhysicalCameraDevice(device); + ReleaseCameraDevice(device); + SDL_SetError("Couldn't create camera thread"); + return NULL; + } + } + + ReleaseCameraDevice(device); // unlock, we're good to go! + + return (SDL_Camera *) device; // currently there's no separation between physical and logical device. +} + +SDL_Surface *SDL_AcquireCameraFrame(SDL_Camera *camera, Uint64 *timestampNS) +{ + if (timestampNS) { + *timestampNS = 0; + } + + if (!camera) { + SDL_InvalidParamError("camera"); + return NULL; + } + + SDL_CameraDevice *device = (SDL_CameraDevice *) camera; // currently there's no separation between physical and logical device. + + 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... + SurfaceList *slistprev = &device->filled_output_surfaces; + SurfaceList *slist = slistprev; + while (slist->next) { + slistprev = slist; + slist = slist->next; + } + + 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; + } + + ReleaseCameraDevice(device); + + return retval; +} + +int SDL_ReleaseCameraFrame(SDL_Camera *camera, SDL_Surface *frame) +{ + if (!camera) { + return SDL_InvalidParamError("camera"); + } else if (frame == NULL) { + return SDL_InvalidParamError("frame"); + } + + SDL_CameraDevice *device = (SDL_CameraDevice *) camera; // currently there's no separation between physical and logical device. + ObtainPhysicalCameraDeviceObj(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; + } + + if (!slist) { + ReleaseCameraDevice(device); + return SDL_SetError("Surface was not acquired from this camera, or was already released"); + } + + // this pointer was owned by the backend (DMA memory or whatever), clear it out. + if (!device->needs_conversion && !device->needs_scaling) { + device->ReleaseFrame(device, frame); + frame->pixels = NULL; + frame->pitch = 0; + } + + 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_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); + } + + return retval; +} + +SDL_PropertiesID SDL_GetCameraProperties(SDL_Camera *camera) +{ + 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 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. + #define FILL_STUB(x) SDL_assert(camera_driver.impl.x != NULL) + FILL_STUB(DetectDevices); + FILL_STUB(OpenDevice); + FILL_STUB(CloseDevice); + FILL_STUB(AcquireFrame); + FILL_STUB(ReleaseFrame); + FILL_STUB(FreeDeviceHandle); + FILL_STUB(Deinitialize); + #undef FILL_STUB +} + +void SDL_QuitCamera(void) +{ + if (!camera_driver.name) { // not initialized?! + return; + } + + 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); + } + + 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); +} + + +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_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) { + 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) { + SDL_DestroyRWLock(device_hash_lock); + SDL_DestroyHashTable(device_hash); + 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); + camera_driver.pending_events_tail = &camera_driver.pending_events; + 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; + 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); + camera_driver.pending_events_tail = &camera_driver.pending_events; + 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; + 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); + SDL_DestroyRWLock(device_hash_lock); + SDL_DestroyHashTable(device_hash); + 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; +} + +// 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/video/SDL_video_capture_c.h b/src/camera/SDL_camera_c.h similarity index 76% rename from src/video/SDL_video_capture_c.h rename to src/camera/SDL_camera_c.h index d7f1aa17d2e9d..6fae3101df737 100644 --- a/src/video/SDL_video_capture_c.h +++ b/src/camera/SDL_camera_c.h @@ -19,15 +19,17 @@ 3. This notice may not be removed or altered from any source distribution. */ #include "../SDL_internal.h" -#include "../../include/SDL3/SDL_video_capture.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(const char *driver_name); -/* Shutdown the video_capture subsystem */ -void SDL_QuitVideoCapture(void); +// Shutdown the camera subsystem +void SDL_QuitCamera(void); -#endif /* SDL_video_capture_c_h_ */ +// "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 new file mode 100644 index 0000000000000..2d1851c75297d --- /dev/null +++ b/src/camera/SDL_syscamera.h @@ -0,0 +1,213 @@ +/* + 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" + +#ifndef SDL_syscamera_h_ +#define SDL_syscamera_h_ + +#include "../SDL_hashtable.h" + +#define DEBUG_CAMERA 0 + +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, 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. */ +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); + +// 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); +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; + Uint64 timestampNS; + struct SurfaceList *next; +} SurfaceList; + +// Define the SDL camera driver structure +struct SDL_CameraDevice +{ + // A mutex for locking + SDL_Mutex *lock; + + // 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; + + // 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; + + // 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; + + // 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; + + // 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; + + // 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; + + // 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 + + // SDL_TRUE if acquire_surface needs to be converted for final output. + SDL_bool needs_conversion; + + // Current state flags + SDL_AtomicInt shutdown; + SDL_AtomicInt zombie; + + // A thread to feed the camera device + SDL_Thread *thread; + + // 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; +}; + +typedef struct SDL_CameraDriverImpl +{ + void (*DetectDevices)(void); + 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 +{ + 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; +extern CameraBootStrap EMSCRIPTENCAMERA_bootstrap; +extern CameraBootStrap MEDIAFOUNDATION_bootstrap; + +#endif // SDL_syscamera_h_ diff --git a/src/camera/android/SDL_camera_android.c b/src/camera/android/SDL_camera_android.c new file mode 100644 index 0000000000000..f96990eef5907 --- /dev/null +++ b/src/camera/android/SDL_camera_android.c @@ -0,0 +1,891 @@ +/* + 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_syscamera.h" +#include "../SDL_camera_c.h" +#include "../../video/SDL_pixels_c.h" +#include "../../thread/SDL_systhread.h" + +#ifdef SDL_CAMERA_DRIVER_ANDROID + +/* + * AndroidManifest.xml: + * + * + * + * 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." + */ + +// 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 +#include + +#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; + +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 SetErrorStr(const char *what, const char *errstr, const int rc) +{ + char errbuf[128]; + if (!errstr) { + SDL_snprintf(errbuf, sizeof (errbuf), "Unknown error #%d", rc); + errstr = errbuf; + } + return SDL_SetError("%s: %s", what, errstr); +} + +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 int SetCameraError(const char *what, const camera_status_t rc) +{ + return SetErrorStr(what, CameraStatusStr(rc), (int) rc); +} + +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 +} + +static int SetMediaError(const char *what, const media_status_t rc) +{ + return SetErrorStr(what, MediaStatusStr(rc), (int) rc); +} + + +static ACameraManager *cameraMgr = NULL; + +static int CreateCameraManager(void) +{ + SDL_assert(cameraMgr == NULL); + + cameraMgr = pACameraManager_create(); + if (!cameraMgr) { + return SDL_SetError("Error creating ACameraManager"); + } + return 0; +} + +static void DestroyCameraManager(void) +{ + if (cameraMgr) { + pACameraManager_delete(cameraMgr); + cameraMgr = NULL; + } +} + +static Uint32 format_android_to_sdl(Uint32 fmt) +{ + switch (fmt) { + #define CASE(x, y) case x: return y + 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); + #undef CASE + 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, 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); + #undef CASE + default: + return 0; + } +} + +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) +{ + #if DEBUG_CAMERA + SDL_Log("CAMERA: CB onDisconnected"); + #endif + SDL_CameraDeviceDisconnected((SDL_CameraDevice *) context); +} + +static void onError(void *context, ACameraDevice *device, int error) +{ + #if DEBUG_CAMERA + SDL_Log("CAMERA: CB onError"); + #endif + SDL_CameraDeviceDisconnected((SDL_CameraDevice *) context); +} + +static void onClosed(void* context, ACameraCaptureSession *session) +{ + // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; + #if DEBUG_CAMERA + SDL_Log("CAMERA: CB onClosed"); + #endif +} + +static void onReady(void* context, ACameraCaptureSession *session) +{ + // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; + #if DEBUG_CAMERA + SDL_Log("CAMERA: CB onReady"); + #endif +} + +static void onActive(void* context, ACameraCaptureSession *session) +{ + // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; + #if DEBUG_CAMERA + SDL_Log("CAMERA: CB onActive"); + #endif +} + +static void ANDROIDCAMERA_CloseDevice(SDL_CameraDevice *device) +{ + if (device && device->hidden) { + struct SDL_PrivateCameraData *hidden = device->hidden; + device->hidden = NULL; + + if (hidden->reader) { + pAImageReader_setImageListener(hidden->reader, NULL); + } + + if (hidden->session) { + pACameraCaptureSession_close(hidden->session); + } + + if (hidden->request) { + pACaptureRequest_free(hidden->request); + } + + if (hidden->outputTarget) { + pACameraOutputTarget_free(hidden->outputTarget); + } + + if (hidden->sessionOutputContainer) { + pACaptureSessionOutputContainer_free(hidden->sessionOutputContainer); + } + + if (hidden->sessionOutput) { + pACaptureSessionOutput_free(hidden->sessionOutput); + } + + // we don't free hidden->window here, it'll be cleaned up by AImageReader_delete. + + if (hidden->reader) { + pAImageReader_delete(hidden->reader); + } + + if (hidden->device) { + pACameraDevice_close(hidden->device); + } + + SDL_free(hidden); + } +} + +// this is where the "opening" of the camera happens, after permission is granted. +static int PrepareCamera(SDL_CameraDevice *device) +{ + SDL_assert(device->hidden != NULL); + + camera_status_t res; + media_status_t res2; + + ACameraDevice_StateCallbacks dev_callbacks; + SDL_zero(dev_callbacks); + dev_callbacks.context = device; + dev_callbacks.onDisconnected = onDisconnected; + dev_callbacks.onError = onError; + + 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; +} + +static void SDLCALL CameraPermissionCallback(void *userdata, const char *permission, SDL_bool granted) +{ + 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! + } + } + + UnrefPhysicalCameraDevice(device); // we ref'd this in OpenDevice, release the extra reference. +} + + +static int ANDROIDCAMERA_OpenDevice(SDL_CameraDevice *device, const SDL_CameraSpec *spec) +{ +#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 + + device->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData)); + if (device->hidden == NULL) { + return -1; + } + + RefPhysicalCameraDevice(device); // ref'd until permission callback fires. + + // 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; + } + + return 0; // we don't open the camera until permission is granted, so always succeed for now. +} + +static void ANDROIDCAMERA_FreeDeviceHandle(SDL_CameraDevice *device) +{ + if (device) { + SDL_free(device->handle); + } +} + +static void GatherCameraSpecs(const char *devid, CameraFormatAddData *add_data, char **fullname, SDL_CameraPosition *position) +{ + 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); + } + } + + 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) { + *position = SDL_CAMERA_POSITION_FRONT_FACING; + if (!*fullname) { + *fullname = SDL_strdup("Front-facing camera"); + } + } else if (*posentry.data.u8 == ACAMERA_LENS_FACING_BACK) { + *position = SDL_CAMERA_POSITION_BACK_FACING; + if (!*fullname) { + *fullname = SDL_strdup("Back-facing camera"); + } + } + } + + if (!*fullname) { + *fullname = SDL_strdup("Generic camera"); // we tried. + } + + 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; + } else if ((w <= 0) || (h <= 0)) { + continue; + } else if ((sdlfmt = format_android_to_sdl(fmt)) == SDL_PIXELFORMAT_UNKNOWN) { + continue; + } + +#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); + } + } +#else + SDL_AddCameraFormat(add_data, sdlfmt, w, h, 1, 30); +#endif + } + + pACameraMetadata_free(metadata); +} + +static SDL_bool FindAndroidCameraDeviceByID(SDL_CameraDevice *device, void *userdata) +{ + const char *devid = (const char *) userdata; + return (SDL_strcmp(devid, (const char *) device->handle) == 0); +} + +static void MaybeAddDevice(const char *devid) +{ + #if DEBUG_CAMERA + SDL_Log("CAMERA: MaybeAddDevice('%s')", devid); + #endif + + if (SDL_FindPhysicalCameraDeviceByCallback(FindAndroidCameraDeviceByID, (void *) devid)) { + return; // already have this one. + } + + SDL_CameraPosition position = SDL_CAMERA_POSITION_UNKNOWN; + char *fullname = NULL; + CameraFormatAddData add_data; + 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, position, add_data.num_specs, add_data.specs, namecpy); + if (!device) { + SDL_free(namecpy); + } + } + } + + SDL_free(fullname); + SDL_free(add_data.specs); +} + +// 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. + +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); +} + +static void onCameraUnavailable(void *context, const char *cameraId) +{ + #if DEBUG_CAMERA + SDL_Log("CAMERA: CB onCameraUnvailable('%s')", cameraId); + #endif + + SDL_assert(cameraId != NULL); + + // 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 = { + NULL, + onCameraAvailable, + onCameraUnavailable +}; + +static void ANDROIDCAMERA_DetectDevices(void) +{ + ACameraIdList *list = NULL; + camera_status_t res = pACameraManager_getCameraIdList(cameraMgr, &list); + + if ((res == ACAMERA_OK) && list) { + const int total = list->numCameras; + for (int i = 0; i < total; i++) { + MaybeAddDevice(list->cameraIds[i]); + } + + pACameraManager_deleteCameraIdList(list); + } + + pACameraManager_registerAvailabilityCallback(cameraMgr, &camera_availability_listener); +} + +static void ANDROIDCAMERA_Deinitialize(void) +{ + pACameraManager_unregisterAvailabilityCallback(cameraMgr, &camera_availability_listener); + DestroyCameraManager(); + + 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) +{ + // !!! 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->WaitDevice = ANDROIDCAMERA_WaitDevice; + impl->AcquireFrame = ANDROIDCAMERA_AcquireFrame; + impl->ReleaseFrame = ANDROIDCAMERA_ReleaseFrame; + impl->FreeDeviceHandle = ANDROIDCAMERA_FreeDeviceHandle; + impl->Deinitialize = ANDROIDCAMERA_Deinitialize; + + impl->ProvidesOwnCallbackThread = SDL_TRUE; + + return SDL_TRUE; +} + +CameraBootStrap ANDROIDCAMERA_bootstrap = { + "android", "SDL Android camera driver", ANDROIDCAMERA_Init, SDL_FALSE +}; + +#endif diff --git a/src/camera/coremedia/SDL_camera_coremedia.m b/src/camera/coremedia/SDL_camera_coremedia.m new file mode 100644 index 0000000000000..049c4a4065d13 --- /dev/null +++ b/src/camera/coremedia/SDL_camera_coremedia.m @@ -0,0 +1,479 @@ +/* + 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_COREMEDIA + +#include "../SDL_syscamera.h" +#include "../SDL_camera_c.h" +#include "../../thread/SDL_systhread.h" + +#import +#import + +/* + * Need to link with:: CoreMedia CoreVideo + * + * Add in pInfo.list: + * NSCameraUsageDescription Access camera + * + * + * MACOSX: + * Add to the Code Sign Entitlement file: + * com.apple.security.device.camera + */ + +static Uint32 CoreMediaFormatToSDL(FourCharCode fmt) +{ + 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; +} + +@class SDLCaptureVideoDataOutputSampleBufferDelegate; + +// 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 + +@implementation SDLPrivateCameraData +@end + + +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 { + SDL_CameraDevicePermissionOutcome(device, SDL_TRUE); // always allowed (or just unqueryable...?) on older macOS. + } + } + + return (device->permission > 0); +} + +// 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 + +@implementation SDLCaptureVideoDataOutputSampleBufferDelegate + + -(id) init:(SDL_CameraDevice *) dev { + if ( self = [super init] ) { + _device = dev; + } + return self; + } + + - (void) captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection + { + SDL_CameraDevice *device = self.device; + if (!device || !device->hidden) { + return; // oh well. + } + + if (!CheckCameraPermissions(device)) { + return; // nothing to do right now, dump what is probably a completely black frame. + } + + SDLPrivateCameraData *hidden = (__bridge SDLPrivateCameraData *) device->hidden; + hidden.current_sample = sampleBuffer; + SDL_CameraThreadIterate(device); + hidden.current_sample = NULL; + } + + - (void)captureOutput:(AVCaptureOutput *)output didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection + { + #if DEBUG_CAMERA + SDL_Log("CAMERA: Drop frame."); + #endif + } +@end + +static int COREMEDIA_WaitDevice(SDL_CameraDevice *device) +{ + 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; + } + + 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); + + #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 + + // !!! 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); + + 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); + } + } 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); + } + buflen *= frame->h; + + 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; + } + } + } + + CVPixelBufferUnlockBaseAddress(image, 0); + + return retval; +} + +static void COREMEDIA_ReleaseFrame(SDL_CameraDevice *device, SDL_Surface *frame) +{ + // !!! 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); +} + +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) { + hidden.session = nil; + [session stopRunning]; + [session removeInput:[session.inputs objectAtIndex:0]]; + [session removeOutput:(AVCaptureVideoDataOutput*)[session.outputs objectAtIndex:0]]; + session = nil; + } + + hidden.delegate = NULL; + hidden.current_sample = NULL; + } +} + +static int COREMEDIA_OpenDevice(SDL_CameraDevice *device, const SDL_CameraSpec *spec) +{ + AVCaptureDevice *avdevice = (__bridge AVCaptureDevice *) device->handle; + + // Pick format that matches the spec + 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]; + 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("camera spec format not available"); + } else if (![avdevice lockForConfiguration:NULL]) { + 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"); + } + + session.sessionPreset = AVCaptureSessionPresetHigh; + + NSError *error = nil; + AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:avdevice error:&error]; + if (!input) { + return SDL_SetError("Cannot create AVCaptureDeviceInput"); + } + + AVCaptureVideoDataOutput *output = [[AVCaptureVideoDataOutput alloc] init]; + if (!output) { + return SDL_SetError("Cannot create AVCaptureVideoDataOutput"); + } + + 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"); + } + + SDLCaptureVideoDataOutputSampleBufferDelegate *delegate = [[SDLCaptureVideoDataOutputSampleBufferDelegate alloc] init:device]; + if (delegate == nil) { + return SDL_SetError("Cannot create SDLCaptureVideoDataOutputSampleBufferDelegate"); + } + [output setSampleBufferDelegate:delegate queue:queue]; + + if (![session canAddInput:input]) { + return SDL_SetError("Cannot add AVCaptureDeviceInput"); + } + [session addInput:input]; + + if (![session canAddOutput:output]) { + return SDL_SetError("Cannot add AVCaptureVideoDataOutput"); + } + [session addOutput:output]; + + [session commitConfiguration]; + + SDLPrivateCameraData *hidden = [[SDLPrivateCameraData alloc] init]; + if (hidden == nil) { + return SDL_SetError("Cannot create SDLPrivateCameraData"); + } + + hidden.session = session; + hidden.delegate = delegate; + hidden.current_sample = NULL; + device->hidden = (struct SDL_PrivateCameraData *)CFBridgingRetain(hidden); + + [session startRunning]; // !!! FIXME: docs say this can block while camera warms up and shouldn't be done on main thread. Maybe push through `queue`? + + CheckCameraPermissions(device); // check right away, in case the process is already granted permission. + + return 0; +} + +static void COREMEDIA_FreeDeviceHandle(SDL_CameraDevice *device) +{ + if (device && device->handle) { + CFBridgingRelease(device->handle); + } +} + +static void GatherCameraSpecs(AVCaptureDevice *device, CameraFormatAddData *add_data) +{ + SDL_zerop(add_data); + + for (AVCaptureDeviceFormat *fmt in device.formats) { + if (CMFormatDescriptionGetMediaType(fmt.formatDescription) != kCMMediaType_Video) { + continue; + } + + const Uint32 sdlfmt = CoreMediaFormatToSDL(CMFormatDescriptionGetMediaSubType(fmt.formatDescription)); + if (sdlfmt == SDL_PIXELFORMAT_UNKNOWN) { + continue; + } + + 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; + + 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); + } + } + } +} + +static SDL_bool FindCoreMediaCameraDeviceByUniqueID(SDL_CameraDevice *device, void *userdata) +{ + NSString *uniqueid = (__bridge NSString *) userdata; + AVCaptureDevice *avdev = (__bridge AVCaptureDevice *) device->handle; + return ([uniqueid isEqualToString:avdev.uniqueID]) ? SDL_TRUE : SDL_FALSE; +} + +static void MaybeAddDevice(AVCaptureDevice *avdevice) +{ + if (!avdevice.connected) { + return; // not connected. + } else if (![avdevice hasMediaType:AVMediaTypeVideo]) { + return; // not a camera. + } else if (SDL_FindPhysicalCameraDeviceByCallback(FindCoreMediaCameraDeviceByUniqueID, (__bridge void *) avdevice.uniqueID)) { + return; // already have this one. + } + + CameraFormatAddData add_data; + GatherCameraSpecs(avdevice, &add_data); + if (add_data.num_specs > 0) { + 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); +} + +static void COREMEDIA_DetectDevices(void) +{ + 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 + ]; + + AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession + discoverySessionWithDeviceTypes:device_types + mediaType:AVMediaTypeVideo + position:AVCaptureDevicePositionUnspecified]; + + 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. + } + + for (AVCaptureDevice *device in devices) { + MaybeAddDevice(device); + } +} + +static void COREMEDIA_Deinitialize(void) +{ + // !!! FIXME: disable hotplug. +} + +static SDL_bool COREMEDIA_Init(SDL_CameraDriverImpl *impl) +{ + impl->DetectDevices = COREMEDIA_DetectDevices; + impl->OpenDevice = COREMEDIA_OpenDevice; + impl->CloseDevice = COREMEDIA_CloseDevice; + impl->WaitDevice = COREMEDIA_WaitDevice; + impl->AcquireFrame = COREMEDIA_AcquireFrame; + impl->ReleaseFrame = COREMEDIA_ReleaseFrame; + impl->FreeDeviceHandle = COREMEDIA_FreeDeviceHandle; + impl->Deinitialize = COREMEDIA_Deinitialize; + + impl->ProvidesOwnCallbackThread = SDL_TRUE; + + return SDL_TRUE; +} + +CameraBootStrap COREMEDIA_bootstrap = { + "coremedia", "SDL Apple CoreMedia camera driver", COREMEDIA_Init, SDL_FALSE +}; + +#endif // SDL_CAMERA_DRIVER_COREMEDIA + diff --git a/src/camera/dummy/SDL_camera_dummy.c b/src/camera/dummy/SDL_camera_dummy.c new file mode 100644 index 0000000000000..b93eecbbe1824 --- /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 // SDL_CAMERA_DRIVER_DUMMY diff --git a/src/camera/emscripten/SDL_camera_emscripten.c b/src/camera/emscripten/SDL_camera_emscripten.c new file mode 100644 index 0000000000000..9a605a7527904 --- /dev/null +++ b/src/camera/emscripten/SDL_camera_emscripten.c @@ -0,0 +1,265 @@ +/* + 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); +} + +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", SDL_CAMERA_POSITION_UNKNOWN, 0, NULL, (void *) (size_t) 0x1); + } +} + +static SDL_bool EMSCRIPTENCAMERA_Init(SDL_CameraDriverImpl *impl) +{ + MAIN_THREAD_EM_ASM({ + if (typeof(Module['SDL3']) === 'undefined') { + Module['SDL3'] = {}; + } + Module['SDL3'].camera = {}; + }); + + 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/mediafoundation/SDL_camera_mediafoundation.c b/src/camera/mediafoundation/SDL_camera_mediafoundation.c new file mode 100644 index 0000000000000..02605013ca4d0 --- /dev/null +++ b/src/camera/mediafoundation/SDL_camera_mediafoundation.c @@ -0,0 +1,927 @@ +/* + 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 } }; + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmultichar" +#endif + +#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 + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +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, (DWORD)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; +} + + +#ifdef 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; +#if 0 + DWORD num_streams = 0; +#endif + LONG lstride = 0; + //PROPVARIANT var; + HRESULT ret; + + #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, (DWORD)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, (void**)&source); + if (SUCCEEDED(ret) && source) { + CameraFormatAddData add_data; + GatherCameraSpecs(source, &add_data); + if (add_data.num_specs > 0) { + SDL_AddCameraDevice(name, SDL_CAMERA_POSITION_UNKNOWN, 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 new file mode 100644 index 0000000000000..07968923d0471 --- /dev/null +++ b/src/camera/v4l2/SDL_camera_v4l2.c @@ -0,0 +1,890 @@ +/* + 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" + +#ifdef SDL_CAMERA_DRIVER_V4L2 + +#include +#include +#include +#include // low-level i/o +#include +#include +#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" +#include "../../thread/SDL_systhread.h" +#include "../../core/linux/SDL_evdev_capabilities.h" +#include "../../core/linux/SDL_udev.h" + +#ifndef SDL_USE_LIBUDEV +#include +#endif + +typedef struct V4L2DeviceHandle +{ + char *bus_info; + char *path; +} V4L2DeviceHandle; + + +typedef enum io_method { + IO_METHOD_INVALID, + IO_METHOD_READ, + IO_METHOD_MMAP, + IO_METHOD_USERPTR +} io_method; + +struct buffer { + void *start; + size_t length; + int available; // Is available in userspace +}; + +struct SDL_PrivateCameraData +{ + int fd; + io_method io; + int nb_buffers; + struct buffer *buffers; + int driver_pitch; +}; + +static int xioctl(int fh, int request, void *arg) +{ + int r; + + do { + r = ioctl(fh, request, arg); + } while ((r == -1) && (errno == EINTR)); + + return r; +} + +static int V4L2_WaitDevice(SDL_CameraDevice *device) +{ + 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, device->hidden->buffers[0].start, size) == -1) { + switch (errno) { + case EAGAIN: + return 0; + + case EIO: + // Could ignore EIO, see spec. + // fall through + + default: + return SDL_SetError("read"); + } + } + + *timestampNS = SDL_GetTicksNS(); // oh well, close enough. + frame->pixels = device->hidden->buffers[0].start; + frame->pitch = device->hidden->driver_pitch; + break; + + case IO_METHOD_MMAP: + SDL_zero(buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + + if (xioctl(fd, VIDIOC_DQBUF, &buf) == -1) { + switch (errno) { + case EAGAIN: + return 0; + + case EIO: + // Could ignore EIO, see spec. + // fall through + + default: + return SDL_SetError("VIDIOC_DQBUF: %d", errno); + } + } + + if ((int)buf.index < 0 || (int)buf.index >= device->hidden->nb_buffers) { + return SDL_SetError("invalid buffer index"); + } + + frame->pixels = device->hidden->buffers[buf.index].start; + 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 + break; + + case IO_METHOD_USERPTR: + SDL_zero(buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_USERPTR; + + if (xioctl(fd, VIDIOC_DQBUF, &buf) == -1) { + switch (errno) { + case EAGAIN: + return 0; + + case EIO: + // Could ignore EIO, see spec. + + // fall through + + default: + return SDL_SetError("VIDIOC_DQBUF"); + } + } + + int i; + 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 >= device->hidden->nb_buffers) { + return SDL_SetError("invalid buffer index"); + } + + frame->pixels = (void*)buf.m.userptr; + 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 + break; + + case IO_METHOD_INVALID: + SDL_assert(!"Shouldn't have hit this"); + break; + } + + return 1; +} + +static void V4L2_ReleaseFrame(SDL_CameraDevice *device, SDL_Surface *frame) +{ + struct v4l2_buffer buf; + const int fd = device->hidden->fd; + const io_method io = device->hidden->io; + int i; + + for (i = 0; i < device->hidden->nb_buffers; ++i) { + if (frame->pixels == device->hidden->buffers[i].start) { + break; + } + } + + if (i >= device->hidden->nb_buffers) { + return; // oh well, we didn't own this. + } + + switch (io) { + case IO_METHOD_READ: + break; + + case IO_METHOD_MMAP: + SDL_zero(buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + + if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) { + // !!! FIXME: disconnect the device. + return; //SDL_SetError("VIDIOC_QBUF"); + } + device->hidden->buffers[i].available = 0; + break; + + case IO_METHOD_USERPTR: + SDL_zero(buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_USERPTR; + buf.index = i; + buf.m.userptr = (unsigned long)frame->pixels; + buf.length = (int) device->hidden->buffers[i].length; + + if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) { + // !!! FIXME: disconnect the device. + return; //SDL_SetError("VIDIOC_QBUF"); + } + device->hidden->buffers[i].available = 0; + break; + + case IO_METHOD_INVALID: + SDL_assert(!"Shouldn't have hit this"); + break; + } +} + +static int EnqueueBuffers(SDL_CameraDevice *device) +{ + 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 < 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_MMAP; + buf.index = i; + + if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) { + return SDL_SetError("VIDIOC_QBUF"); + } + } + } + break; + + case IO_METHOD_USERPTR: + 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)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"); + } + } + } + break; + + case IO_METHOD_INVALID: SDL_assert(!"Shouldn't have hit this"); break; + } + return 0; +} + +static int AllocBufferRead(SDL_CameraDevice *device, size_t buffer_size) +{ + 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 *device) +{ + const int fd = device->hidden->fd; + int i; + for (i = 0; i < device->hidden->nb_buffers; ++i) { + struct v4l2_buffer buf; + + SDL_zero(buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + + if (xioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) { + return SDL_SetError("VIDIOC_QUERYBUF"); + } + + 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 == device->hidden->buffers[i].start) { + return SDL_SetError("mmap"); + } + } + return 0; +} + +static int AllocBufferUserPtr(SDL_CameraDevice *device, size_t buffer_size) +{ + int i; + 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 (!device->hidden->buffers[i].start) { + return -1; + } + } + return 0; +} + +static Uint32 format_v4l2_to_sdl(Uint32 fmt) +{ + switch (fmt) { + #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 + default: + #if DEBUG_CAMERA + SDL_Log("CAMERA: Unknown format V4L2_PIX_FORMAT '%d'", fmt); + #endif + return SDL_PIXELFORMAT_UNKNOWN; + } +} + +static Uint32 format_sdl_to_v4l2(Uint32 fmt) +{ + switch (fmt) { + #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 + default: + return 0; + } +} + +static void V4L2_CloseDevice(SDL_CameraDevice *device) +{ + if (!device) { + return; + } + + if (device->hidden) { + const io_method io = device->hidden->io; + const int fd = device->hidden->fd; + + if ((io == IO_METHOD_MMAP) || (io == IO_METHOD_USERPTR)) { + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + xioctl(fd, VIDIOC_STREAMOFF, &type); + } + + if (device->hidden->buffers) { + switch (io) { + case IO_METHOD_INVALID: + break; + + case IO_METHOD_READ: + SDL_free(device->hidden->buffers[0].start); + break; + + case IO_METHOD_MMAP: + 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 < device->hidden->nb_buffers; ++i) { + SDL_free(device->hidden->buffers[i].start); + } + break; + } + + SDL_free(device->hidden->buffers); + } + + if (fd != -1) { + close(fd); + } + SDL_free(device->hidden); + + device->hidden = NULL; + } +} + +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; + 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); + } + + device->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData)); + if (device->hidden == NULL) { + close(fd); + return -1; + } + + device->hidden->fd = fd; + device->hidden->io = IO_METHOD_INVALID; + + // 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); + } + + 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"); + } + + 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) { + 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 ((io == IO_METHOD_INVALID) && (cap.device_caps & V4L2_CAP_READWRITE)) { + io = IO_METHOD_READ; + device->hidden->nb_buffers = 1; + } + + if (io == IO_METHOD_INVALID) { + return SDL_SetError("Don't have a way to talk to this device"); + } + + device->hidden->io = io; + + device->hidden->buffers = SDL_calloc(device->hidden->nb_buffers, sizeof(*device->hidden->buffers)); + if (!device->hidden->buffers) { + return -1; + } + + 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: + rc = AllocBufferRead(device, size); + break; + + case IO_METHOD_MMAP: + rc = AllocBufferMmap(device); + break; + + case IO_METHOD_USERPTR: + rc = AllocBufferUserPtr(device, size); + break; + + case IO_METHOD_INVALID: + SDL_assert(!"Shouldn't have hit this"); + break; + } + + 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"); + } + } + + // 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; +} + +static SDL_bool FindV4L2CameraDeviceByBusInfoCallback(SDL_CameraDevice *device, void *userdata) +{ + const V4L2DeviceHandle *handle = (const V4L2DeviceHandle *) device->handle; + return (SDL_strcmp(handle->bus_info, (const char *) userdata) == 0); +} + +static int AddCameraFormat(const int fd, CameraFormatAddData *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 (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. + } 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 (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; + } + break; + } + } + + return 0; +} + + +static void MaybeAddDevice(const char *path) +{ + if (!path) { + return; + } + + 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. + } + + 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. + } + + #if DEBUG_CAMERA + SDL_Log("CAMERA: V4L2 camera path='%s' bus_info='%s' name='%s'", path, (const char *) vcap.bus_info, vcap.card); + #endif + + CameraFormatAddData 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); + + #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 + + fmtdesc.index++; // prepare for next iteration. + + if (sdlfmt == SDL_PIXELFORMAT_UNKNOWN) { + continue; // unsupported by SDL atm. + } + + struct v4l2_frmsizeenum frmsizeenum; + SDL_zero(frmsizeenum); + 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 (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. + } 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 (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; + } + } + } + + close(fd); + + #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, SDL_CAMERA_POSITION_UNKNOWN, 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); +} + +static void V4L2_FreeDeviceHandle(SDL_CameraDevice *device) +{ + if (device) { + V4L2DeviceHandle *handle = (V4L2DeviceHandle *) device->handle; + SDL_free(handle->path); + SDL_free(handle->bus_info); + SDL_free(handle); + } +} + +#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); +} + +static void MaybeRemoveDevice(const char *path) +{ + if (path) { + SDL_CameraDeviceDisconnected(SDL_FindPhysicalCameraDeviceByCallback(FindV4L2CameraDeviceByPathCallback, (void *) path)); + } +} + +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); + } + } +} +#endif // SDL_USE_LIBUDEV + +static void V4L2_Deinitialize(void) +{ +#ifdef SDL_USE_LIBUDEV + SDL_UDEV_DelCallback(CameraUdevCallback); + SDL_UDEV_Quit(); +#endif // SDL_USE_LIBUDEV +} + +static void V4L2_DetectDevices(void) +{ +#ifdef SDL_USE_LIBUDEV + if (SDL_UDEV_Init() == 0) { + if (SDL_UDEV_AddCallback(CameraUdevCallback) == 0) { + SDL_UDEV_Scan(); // Force a scan to build the initial device list + } + } +#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); + } +#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->WaitDevice = V4L2_WaitDevice; + impl->AcquireFrame = V4L2_AcquireFrame; + impl->ReleaseFrame = V4L2_ReleaseFrame; + impl->FreeDeviceHandle = V4L2_FreeDeviceHandle; + impl->Deinitialize = V4L2_Deinitialize; + + return SDL_TRUE; +} + +CameraBootStrap V4L2_bootstrap = { + "v4l2", "SDL Video4Linux2 camera driver", V4L2_Init, SDL_FALSE +}; + +#endif // SDL_CAMERA_DRIVER_V4L2 + 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) { 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/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 2ba1f7be6a874..5d08979e090cd 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; @@ -895,22 +894,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; @@ -972,6 +955,25 @@ SDL3_0.0.0 { SDL_RenderGeometryRawFloat; SDL_SetWindowShape; SDL_RenderViewportSet; + SDL_HasProperty; + SDL_GetNumCameraDrivers; + SDL_GetCameraDriver; + SDL_GetCurrentCameraDriver; + SDL_GetCameraDevices; + SDL_GetCameraDeviceSupportedFormats; + SDL_GetCameraDeviceName; + SDL_OpenCameraDevice; + SDL_GetCameraInstanceID; + SDL_GetCameraProperties; + SDL_GetCameraFormat; + SDL_AcquireCameraFrame; + SDL_ReleaseCameraFrame; + SDL_CloseCamera; + SDL_GetCameraPermissionState; + 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 b297a3026ecfc..c5378f1fd77fb 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 @@ -920,22 +919,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 @@ -997,3 +980,22 @@ #define SDL_RenderGeometryRawFloat SDL_RenderGeometryRawFloat_REAL #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_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_GetCameraFormat SDL_GetCameraFormat_REAL +#define SDL_AcquireCameraFrame SDL_AcquireCameraFrame_REAL +#define SDL_ReleaseCameraFrame SDL_ReleaseCameraFrame_REAL +#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 +#define SDL_AddVulkanRenderSemaphores SDL_AddVulkanRenderSemaphores_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 3969fda9c95ff..b19f615cb83f4 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),) @@ -953,22 +952,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) @@ -1012,13 +995,32 @@ 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) 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) +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_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_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),) +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/events/SDL_events.c b/src/events/SDL_events.c index 02600437c49d9..baaff2c992b65 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,21 @@ 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; + 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) (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 +958,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/src/events/SDL_mouse.c b/src/events/SDL_mouse.c index a3888371f16c8..c7cf857cb4912 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); } } } @@ -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; } } } 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") 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; diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c index 6574b66554f78..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; } /* @@ -3750,10 +3815,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) @@ -3770,8 +3836,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; } diff --git a/src/joystick/SDL_gamepad_db.h b/src/joystick/SDL_gamepad_db.h index 7497f1fd60bfe..4e3880fb885e6 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,", @@ -290,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,", diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index bedd229be4421..467a86f7dc8f4 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -49,13 +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 */ +#ifdef SDL_JOYSTICK_GAMEINPUT /* Higher priority than other Windows drivers */ + &SDL_GAMEINPUT_JoystickDriver, +#endif +#ifdef SDL_JOYSTICK_RAWINPUT &SDL_RAWINPUT_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 */ +#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) &SDL_WINDOWS_JoystickDriver, #endif #ifdef SDL_JOYSTICK_WGI @@ -657,6 +660,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; @@ -2564,7 +2590,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); @@ -2580,7 +2606,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; } 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 6ecbab8e58bf9..bc1f8943ebf1a 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); @@ -249,6 +252,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; #ifdef SDL_JOYSTICK_MORPHOS extern SDL_JoystickDriver SDL_MORPHOS_JoystickDriver; #endif 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 new file mode 100644 index 0000000000000..c3ee5a2e776bf --- /dev/null +++ b/src/joystick/gdk/SDL_gameinputjoystick.c @@ -0,0 +1,723 @@ +/* + 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" + +#ifdef SDL_JOYSTICK_GAMEINPUT + +#include "../SDL_sysjoystick.h" +#include "../usb_ids.h" + +#include +#define COBJMACROS +#include + + +typedef struct GAMEINPUT_InternalDevice +{ + IGameInputDevice *device; + char path[(APP_LOCAL_DEVICE_ID_SIZE * 2) + 1]; + char *name; + SDL_JoystickGUID guid; /* generated by SDL */ + SDL_JoystickID device_instance; /* generated by SDL */ + const GameInputDeviceInfo *info; + SDL_bool isAdded; + SDL_bool isDeleteRequested; +} GAMEINPUT_InternalDevice; + +typedef struct GAMEINPUT_InternalList +{ + GAMEINPUT_InternalDevice **devices; + int count; +} GAMEINPUT_InternalList; + +typedef struct joystick_hwdata +{ + GAMEINPUT_InternalDevice *devref; + SDL_bool report_sensors; + GameInputRumbleParams rumbleParams; + GameInputCallbackToken guide_button_callback_token; +} GAMEINPUT_InternalJoystickHwdata; + + +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; + + +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; + GAMEINPUT_InternalDevice *elem = NULL; + const GameInputDeviceInfo *info = NULL; + Uint16 bus = SDL_HARDWARE_BUS_USB; + 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; + + info = IGameInputDevice_GetDeviceInfo(pDevice); + if (info->capabilities & GameInputDeviceCapabilityWireless) { + bus = SDL_HARDWARE_BUS_BLUETOOTH; + } else { + bus = SDL_HARDWARE_BUS_USB; + } + 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; + } + + for (idx = 0; idx < g_GameInputList.count; ++idx) { + elem = g_GameInputList.devices[idx]; + if (elem && elem->device == pDevice) { + /* we're already added */ + return 0; + } + } + + elem = (GAMEINPUT_InternalDevice *)SDL_calloc(1, sizeof(*elem)); + if (!elem) { + 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 path */ + for (idx = 0; idx < APP_LOCAL_DEVICE_ID_SIZE; ++idx) { + SDL_snprintf(tmp, SDL_arraysize(tmp), "%02hhX", info->deviceId.value[idx]); + SDL_strlcat(elem->path, tmp, SDL_arraysize(tmp)); + } + + 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 (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->guid = SDL_CreateJoystickGUID(bus, vendor, product, version, manufacturer_string, product_string, 'g', 0); + elem->device_instance = SDL_GetNextObjectID(); + elem->info = info; + + g_GameInputList.devices = devicelist; + g_GameInputList.devices[g_GameInputList.count++] = elem; + + return 0; +} + +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); + } + + 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 */ + 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); + } + } + + /* decrement the count and return */ + return g_GameInputList.count--; +} + +static GAMEINPUT_InternalDevice *GAMEINPUT_InternalFindByIndex(int idx) +{ + /* We're guaranteed that the index is in range when this is called */ + 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 (!device) { + /* This should never happen, but ignore it if it does */ + return; + } + + 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 = SDL_TRUE; + break; + } + } + } +} + +static void GAMEINPUT_JoystickDetect(void); + +static int GAMEINPUT_JoystickInit(void) +{ + HRESULT hR; + + if (!g_hGameInputDLL) { + g_hGameInputDLL = SDL_LoadObject("gameinput.dll"); + if (!g_hGameInputDLL) { + return -1; + } + } + + if (!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 = IGameInput_RegisterDeviceCallback(g_pGameInput, + NULL, + GameInputKindController, + GameInputDeviceConnected, + GameInputBlockingEnumeration, + NULL, + GAMEINPUT_InternalJoystickDeviceCallback, + &g_GameInputCallbackToken); + if (FAILED(hR)) { + return SDL_SetError("IGameInput::RegisterDeviceCallback failure with HRESULT of %08X", hR); + } + + GAMEINPUT_JoystickDetect(); + + 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->device_instance); + elem->isAdded = SDL_TRUE; + } + + if (elem->isDeleteRequested || !(IGameInputDevice_GetDeviceStatus(elem->device) & GameInputDeviceConnected)) { + SDL_PrivateJoystickRemoved(elem->device_instance); + GAMEINPUT_InternalRemoveByIndex(idx--); + } + } +} + +static SDL_bool GAMEINPUT_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + 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->info->vendorId && product_id == elem->info->productId) { + return SDL_TRUE; + } + } + return SDL_FALSE; +} + +static const char *GAMEINPUT_JoystickGetDeviceName(int device_index) +{ + return GAMEINPUT_InternalFindByIndex(device_index)->name; +} + +static const char *GAMEINPUT_JoystickGetDevicePath(int device_index) +{ + /* APP_LOCAL_DEVICE_ID as a hex string, since it's required for some association callbacks */ + return GAMEINPUT_InternalFindByIndex(device_index)->path; +} + +static int GAMEINPUT_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index) +{ + /* Steamworks API is not available in GDK */ + return -1; +} + +static int GAMEINPUT_JoystickGetDevicePlayerIndex(int device_index) +{ + return -1; +} + +static void GAMEINPUT_JoystickSetDevicePlayerIndex(int device_index, int player_index) +{ +} + +static SDL_JoystickGUID GAMEINPUT_JoystickGetDeviceGUID(int device_index) +{ + return GAMEINPUT_InternalFindByIndex(device_index)->guid; +} + +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; +} + +#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); + const GameInputDeviceInfo *info = elem->info; + 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; + 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 (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 (info->supportedRumbleMotors & (GameInputRumbleLowFrequency | GameInputRumbleHighFrequency)) { + SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, SDL_TRUE); + } + if (info->supportedRumbleMotors & (GameInputRumbleLeftTrigger | GameInputRumbleRightTrigger)) { + SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, SDL_TRUE); + } + + if (info->supportedInput & GameInputKindTouch) { + SDL_PrivateJoystickAddTouchpad(joystick, info->touchPointCount); + } + + 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 (info->capabilities & GameInputDeviceCapabilityWireless) { + joystick->epowerlevel = GAMEINPUT_InternalGetPowerLevel(elem->device); + } else { + joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; + } + 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; + IGameInputDevice_SetRumbleState(hwdata->devref->device, 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; + IGameInputDevice_SetRumbleState(hwdata->devref->device, params); + return 0; +} + +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) +{ + return SDL_Unsupported(); +} + +static int GAMEINPUT_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) +{ + joystick->hwdata->report_sensors = enabled; + return 0; +} + +static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) +{ + 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; + HRESULT hR; + + 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; + } + + /* FIXME: See if we can get the delta between the reading timestamp and current time and apply the offset to timestamp */ + + 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); + } + + 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)); +#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 + } + } 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) { + 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; + + if (IGameInputReading_GetMotionState(reading, &motion_state)) { + /* FIXME: How do we interpret the motion data? */ + } + } + + IGameInputReading_Release(reading); + + 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) +{ + 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; +} + +static void GAMEINPUT_JoystickQuit(void) +{ + if (g_pGameInput) { + /* free the callback */ + IGameInput_UnregisterCallback(g_pGameInput, g_GameInputCallbackToken, /*timeoutInUs:*/ 10000); + g_GameInputCallbackToken = GAMEINPUT_INVALID_CALLBACK_TOKEN_VALUE; + + /* free the list */ + while (g_GameInputList.count > 0) { + GAMEINPUT_InternalRemoveByIndex(0); + } + + IGameInput_Release(g_pGameInput); + g_pGameInput = NULL; + } + + if (g_hGameInputDLL) { + SDL_UnloadObject(g_hGameInputDLL); + g_hGameInputDLL = NULL; + } +} + +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; + + 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; +} + + +SDL_JoystickDriver SDL_GAMEINPUT_JoystickDriver = +{ + GAMEINPUT_JoystickInit, + GAMEINPUT_JoystickGetCount, + GAMEINPUT_JoystickDetect, + GAMEINPUT_JoystickIsDevicePresent, + GAMEINPUT_JoystickGetDeviceName, + GAMEINPUT_JoystickGetDevicePath, + GAMEINPUT_JoystickGetDeviceSteamVirtualGamepadSlot, + GAMEINPUT_JoystickGetDevicePlayerIndex, + GAMEINPUT_JoystickSetDevicePlayerIndex, + GAMEINPUT_JoystickGetDeviceGUID, + GAMEINPUT_JoystickGetDeviceInstanceID, + GAMEINPUT_JoystickOpen, + GAMEINPUT_JoystickRumble, + GAMEINPUT_JoystickRumbleTriggers, + GAMEINPUT_JoystickSetLED, + GAMEINPUT_JoystickSendEffect, + GAMEINPUT_JoystickSetSensorsEnabled, + GAMEINPUT_JoystickUpdate, + GAMEINPUT_JoystickClose, + GAMEINPUT_JoystickQuit, + GAMEINPUT_JoystickGetGamepadMapping +}; + + +#endif /* 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..10f074414364d 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); @@ -363,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) { @@ -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..cb788986c949d 100644 --- a/src/joystick/windows/SDL_windows_gaming_input.c +++ b/src/joystick/windows/SDL_windows_gaming_input.c @@ -106,7 +106,6 @@ 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) @@ -448,23 +447,12 @@ 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)) { + if (!ignore_joystick && SDL_JoystickHandledByAnotherDriver(&SDL_WGI_JoystickDriver, vendor, product, version, name)) { 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; } @@ -684,6 +672,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 +1003,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); 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); diff --git a/src/render/SDL_d3dmath.c b/src/render/SDL_d3dmath.c index 664f615b91345..ade1adebef3d7 100644 --- a/src/render/SDL_d3dmath.c +++ b/src/render/SDL_d3dmath.c @@ -20,7 +20,7 @@ */ #include "SDL_internal.h" -#if (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11 || SDL_VIDEO_RENDER_D3D12) +#if (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11 || SDL_VIDEO_RENDER_D3D12 || SDL_VIDEO_RENDER_VULKAN) #include "SDL_d3dmath.h" diff --git a/src/render/SDL_d3dmath.h b/src/render/SDL_d3dmath.h index b1ca3517ab8c1..ab8d2895b3f12 100644 --- a/src/render/SDL_d3dmath.h +++ b/src/render/SDL_d3dmath.h @@ -20,7 +20,7 @@ */ #include "SDL_internal.h" -#if (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11 || SDL_VIDEO_RENDER_D3D12) +#if (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11 || SDL_VIDEO_RENDER_D3D12 || SDL_VIDEO_RENDER_VULKAN) /* Set up for C function definitions, even when using C++ */ #ifdef __cplusplus @@ -78,4 +78,4 @@ Float4X4 MatrixRotationZ(float r); } #endif -#endif /* (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11 || SDL_VIDEO_RENDER_D3D12) */ +#endif /* (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11 || SDL_VIDEO_RENDER_D3D12 || SDL_VIDEO_RENDER_VULKAN)*/ diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 92bc539eb5c1b..a6a785bbf7ff3 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) { \ @@ -89,12 +90,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 @@ -116,6 +117,9 @@ static const SDL_RenderDriver *render_drivers[] = { #if SDL_VIDEO_RENDER_VITA_GXM &VITA_GXM_RenderDriver, #endif +#if SDL_VIDEO_RENDER_VULKAN + &VULKAN_RenderDriver, +#endif #if SDL_VIDEO_RENDER_SW &SW_RenderDriver #endif @@ -150,7 +154,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; @@ -482,31 +486,6 @@ static int QueueCmdSetDrawColor(SDL_Renderer *renderer, SDL_FColor *color) return retval; } -static int QueueCmdSetColorScale(SDL_Renderer *renderer) -{ - int retval = 0; - - if (!renderer->color_scale_queued || - renderer->color_scale != renderer->last_queued_color_scale) { - SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); - retval = -1; - - if (cmd) { - cmd->command = SDL_RENDERCMD_SETCOLORSCALE; - cmd->data.color.first = 0; /* render backend will fill this in. */ - cmd->data.color.color_scale = renderer->color_scale; - retval = renderer->QueueSetColorScale(renderer, cmd); - if (retval < 0) { - cmd->command = SDL_RENDERCMD_NO_OP; - } else { - renderer->last_queued_color_scale = renderer->color_scale; - renderer->color_scale_queued = SDL_TRUE; - } - } - } - return retval; -} - static int QueueCmdClear(SDL_Renderer *renderer) { SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); @@ -540,10 +519,6 @@ static SDL_RenderCommand *PrepQueueCmdDraw(SDL_Renderer *renderer, const SDL_Ren retval = QueueCmdSetDrawColor(renderer, color); } - if (retval == 0) { - retval = QueueCmdSetColorScale(renderer); - } - /* Set the viewport and clip rect directly before draws, so the backends * don't have to worry about that state not being valid at draw time. */ if (retval == 0 && !renderer->viewport_queued) { @@ -737,6 +712,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 +808,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 +1007,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 +1030,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); @@ -1164,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) { @@ -1176,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 */ @@ -1186,9 +1209,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]; } } @@ -1271,6 +1300,9 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert } renderer->textures = texture; + texture->SDR_white_point = SDL_GetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, SDL_GetDefaultSDRWhitePoint(texture->colorspace)); + texture->HDR_headroom = SDL_GetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, SDL_GetDefaultHDRHeadroom(texture->colorspace)); + /* 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)); @@ -1302,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) { @@ -1335,6 +1369,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 +1401,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,22 +1429,23 @@ 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 */ 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; @@ -1410,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; @@ -1418,10 +1462,20 @@ 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 < 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))) { - 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; @@ -1432,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]; @@ -1441,7 +1495,18 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s } } - if (format == surface->format->format) { + if (surface_colorspace == SDL_COLORSPACE_SRGB_LINEAR || + SDL_COLORSPACETRANSFER(surface_colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) { + if (SDL_ISPIXELFORMAT_FLOAT(format)) { + texture_colorspace = SDL_COLORSPACE_SRGB_LINEAR; + } else if (SDL_ISPIXELFORMAT_10BIT(format)) { + texture_colorspace = SDL_COLORSPACE_HDR10; + } else { + texture_colorspace = SDL_COLORSPACE_SRGB; + } + } + + if (format == surface->format->format && texture_colorspace == surface_colorspace) { if (surface->format->Amask && SDL_SurfaceHasColorKey(surface)) { /* Surface and Renderer formats are identical. * Intermediate conversion is needed to convert color key to alpha (SDL_ConvertColorkeyToAlpha()). */ @@ -1455,17 +1520,20 @@ 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_ISPIXELFORMAT_FLOAT(format)) { - colorspace = SDL_COLORSPACE_SCRGB; - } else { - 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 +1555,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); @@ -2291,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); } } @@ -2888,7 +2956,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 +2965,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 +4297,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 +4316,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) @@ -4484,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 8d12b5d271f2a..99889f3f88170 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 */ @@ -99,7 +101,6 @@ typedef enum SDL_RENDERCMD_SETVIEWPORT, SDL_RENDERCMD_SETCLIPRECT, SDL_RENDERCMD_SETDRAWCOLOR, - SDL_RENDERCMD_SETCOLORSCALE, SDL_RENDERCMD_CLEAR, SDL_RENDERCMD_DRAW_POINTS, SDL_RENDERCMD_DRAW_LINES, @@ -167,7 +168,6 @@ struct SDL_Renderer int (*CreateTexture)(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props); int (*QueueSetViewport)(SDL_Renderer *renderer, SDL_RenderCommand *cmd); int (*QueueSetDrawColor)(SDL_Renderer *renderer, SDL_RenderCommand *cmd); - int (*QueueSetColorScale)(SDL_Renderer *renderer, SDL_RenderCommand *cmd); int (*QueueDrawPoints)(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count); int (*QueueDrawLines)(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, @@ -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; @@ -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 */ @@ -303,6 +307,7 @@ extern SDL_RenderDriver D3D12_RenderDriver; extern SDL_RenderDriver GL_RenderDriver; extern SDL_RenderDriver GLES2_RenderDriver; extern SDL_RenderDriver METAL_RenderDriver; +extern SDL_RenderDriver VULKAN_RenderDriver; extern SDL_RenderDriver PS2_RenderDriver; extern SDL_RenderDriver PSP_RenderDriver; extern SDL_RenderDriver SW_RenderDriver; diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c index 23c7d9e1173c0..1d83139782e14 100644 --- a/src/render/direct3d/SDL_render_d3d.c +++ b/src/render/direct3d/SDL_render_d3d.c @@ -1181,17 +1181,13 @@ static int D3D_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, v break; } - case SDL_RENDERCMD_SETCOLORSCALE: - { - break; - } - case SDL_RENDERCMD_SETVIEWPORT: { SDL_Rect *viewport = &data->drawstate.viewport; 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; } @@ -1624,7 +1620,6 @@ SDL_Renderer *D3D_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_pro renderer->SetRenderTarget = D3D_SetRenderTarget; renderer->QueueSetViewport = D3D_QueueNoOp; renderer->QueueSetDrawColor = D3D_QueueNoOp; - renderer->QueueSetColorScale = D3D_QueueNoOp; renderer->QueueDrawPoints = D3D_QueueDrawPoints; renderer->QueueDrawLines = D3D_QueueDrawPoints; /* lines and points queue vertices the same way. */ renderer->QueueGeometry = D3D_QueueGeometry; 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..aad7b77a112e0 --- /dev/null +++ b/src/render/direct3d11/D3D11_PixelShader_Advanced.hlsl @@ -0,0 +1,7 @@ + +#include "D3D11_PixelShader_Common.hlsli" + +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_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.hlsli b/src/render/direct3d11/D3D11_PixelShader_Common.hlsli new file mode 100644 index 0000000000000..61053d8c476dc --- /dev/null +++ b/src/render/direct3d11/D3D11_PixelShader_Common.hlsli @@ -0,0 +1,235 @@ + +Texture2D texture0 : register(t0); +Texture2D texture1 : register(t1); +Texture2D texture2 : register(t2); +SamplerState sampler0 : register(s0); + +struct PixelShaderInput +{ + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + 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 tonemap_method; + float tonemap_factor1; + float tonemap_factor2; + float sdr_white_point; + + float4 Yoffset; + float4 Rcoeff; + float4 Gcoeff; + float4 Bcoeff; +}; + +static const float3x3 mat709to2020 = { + { 0.627404, 0.329283, 0.043313 }, + { 0.069097, 0.919541, 0.011362 }, + { 0.016391, 0.088013, 0.895595 } +}; + +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) +{ + if (v <= 0.04045) { + v = (v / 12.92); + } else { + v = pow(abs(v + 0.055) / 1.055, 2.4); + } + return v; +} + +float sRGBfromLinear(float v) +{ + if (v <= 0.0031308) { + v = (v * 12.92); + } else { + v = (pow(abs(v), 1.0 / 2.4) * 1.055 - 0.055); + } + 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; + + output.rgb = rgba.rgb * color_scale; + output.a = rgba.a; + + return output; +} + +float3 GetOutputColorFromSRGB(float3 rgb) +{ + float3 output; + + if (scRGB_output) { + rgb.r = sRGBtoLinear(rgb.r); + rgb.g = sRGBtoLinear(rgb.g); + rgb.b = sRGBtoLinear(rgb.b); + } + + output.rgb = rgb * color_scale; + + return output; +} + +float3 GetOutputColorFromLinear(float3 rgb) +{ + float3 output; + + output.rgb = rgb * color_scale; + + if (!scRGB_output) { + output.r = sRGBfromLinear(output.r); + output.g = sRGBfromLinear(output.g); + output.b = sRGBfromLinear(output.b); + output.rgb = saturate(output.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_Common.incl b/src/render/direct3d11/D3D11_PixelShader_Common.incl deleted file mode 100644 index 35623b762f179..0000000000000 --- a/src/render/direct3d11/D3D11_PixelShader_Common.incl +++ /dev/null @@ -1,92 +0,0 @@ -struct PixelShaderInput -{ - float4 pos : SV_POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; -}; - -cbuffer Constants : register(b0) -{ - float scRGB_output; - float color_scale; - float unused1; - float unused2; - - float4 Yoffset; - float4 Rcoeff; - float4 Gcoeff; - float4 Bcoeff; -}; - -float3 scRGBtoNits(float3 v) -{ - return v * 80.0; -} - -float3 scRGBfromNits(float3 v) -{ - return v / 80.0; -} - -float sRGBtoLinear(float v) -{ - if (v <= 0.04045) { - v = (v / 12.92); - } else { - v = pow(abs(v + 0.055) / 1.055, 2.4); - } - return v; -} - -float sRGBfromLinear(float v) -{ - if (v <= 0.0031308) { - v = (v * 12.92); - } else { - v = (pow(abs(v), 1.0 / 2.4) * 1.055 - 0.055); - } - return v; -} - -float4 GetOutputColor(float4 rgba) -{ - float4 output; - - output.rgb = rgba.rgb * color_scale; - output.a = rgba.a; - - return output; -} - -float4 GetOutputColorFromSRGB(float3 rgb) -{ - float4 output; - - if (scRGB_output) { - rgb.r = sRGBtoLinear(rgb.r); - rgb.g = sRGBtoLinear(rgb.g); - rgb.b = sRGBtoLinear(rgb.b); - } - - output.rgb = rgb * color_scale; - output.a = 1.0; - - return output; -} - -float4 GetOutputColorFromSCRGB(float3 rgb) -{ - float4 output; - - output.rgb = rgb * color_scale; - output.a = 1.0; - - if (!scRGB_output) { - output.r = sRGBfromLinear(output.r); - output.g = sRGBfromLinear(output.g); - output.b = sRGBfromLinear(output.b); - output.rgb = saturate(output.rgb); - } - - return output; -} 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_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/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 ff5435000c013..7297e632e5b3f 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.hlsli */ +//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,13 +265,15 @@ 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_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; @@ -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,13 +295,15 @@ 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_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; @@ -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; @@ -880,7 +901,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 +1000,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: @@ -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,12 +1546,16 @@ 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; - 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); @@ -2170,8 +2172,70 @@ 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 (texture->colorspace == SDL_COLORSPACE_HDR10) { + 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 +2248,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 +2323,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 +2359,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 +2393,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 +2415,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 +2424,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); } @@ -2418,17 +2478,13 @@ static int D3D11_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, break; /* this isn't currently used in this render backend. */ } - case SDL_RENDERCMD_SETCOLORSCALE: - { - break; - } - case SDL_RENDERCMD_SETVIEWPORT: { SDL_Rect *viewport = &rendererData->currentViewport; 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; } @@ -2699,7 +2755,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); @@ -2728,7 +2784,6 @@ SDL_Renderer *D3D11_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_p renderer->SetRenderTarget = D3D11_SetRenderTarget; renderer->QueueSetViewport = D3D11_QueueNoOp; renderer->QueueSetDrawColor = D3D11_QueueNoOp; - renderer->QueueSetColorScale = D3D11_QueueNoOp; renderer->QueueDrawPoints = D3D11_QueueDrawPoints; renderer->QueueDrawLines = D3D11_QueueDrawPoints; /* lines and points queue vertices the same way. */ renderer->QueueGeometry = D3D11_QueueGeometry; @@ -2791,13 +2846,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 + +;