Skip to content
This repository has been archived by the owner on Apr 26, 2023. It is now read-only.

Commit

Permalink
Get IDirect3dDevice from LearningModelDevice
Browse files Browse the repository at this point in the history
Closes Toufool#175

Co-authored-by: Algomancer <[email protected]>
  • Loading branch information
Avasam and Algomancer committed Oct 19, 2022
1 parent e6f1463 commit 220b77f
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 26 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ This program can be used to automatically start, split, and reset your preferred
The smaller the selected region, the more efficient it is.
- **Windows Graphics Capture** (fast, most compatible, capped at 60fps)
Only available in Windows 10.0.17134 and up.
Due to current technical limitations, it requires having at least one audio or video Capture Device connected and enabled. Even if it won't be used.
Due to current technical limitations, Windows versions below 10.0.0.17763 require having at least one audio or video Capture Device connected and enabled.
Allows recording UWP apps, Hardware Accelerated and Exclusive Fullscreen windows.
Adds a yellow border on Windows 10 (not on Windows 11).
Caps at around 60 FPS.
Expand Down
16 changes: 3 additions & 13 deletions src/capture_method/WindowsGraphicsCaptureMethod.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@
from winsdk.windows.graphics.capture.interop import create_for_window
from winsdk.windows.graphics.directx import DirectXPixelFormat
from winsdk.windows.graphics.imaging import BitmapBufferAccessMode, SoftwareBitmap
from winsdk.windows.media.capture import MediaCapture

from capture_method.CaptureMethodBase import CaptureMethodBase
from utils import WINDOWS_BUILD_NUMBER, is_valid_hwnd
from utils import WINDOWS_BUILD_NUMBER, get_direct3d_device, is_valid_hwnd

if TYPE_CHECKING:
from AutoSplit import AutoSplit
Expand All @@ -33,19 +32,10 @@ def __init__(self, autosplit: AutoSplit):
super().__init__(autosplit)
if not is_valid_hwnd(autosplit.hwnd):
return
# Note: Must create in the same thread (can't use a global) otherwise when ran from LiveSplit it will raise:
# OSError: The application called an interface that was marshalled for a different thread
media_capture = MediaCapture()
item = create_for_window(autosplit.hwnd)

async def coroutine():
await (media_capture.initialize_async() or asyncio.sleep(0))
asyncio.run(coroutine())

if not media_capture.media_capture_settings:
raise OSError("Unable to initialize a Direct3D Device.")
item = create_for_window(autosplit.hwnd)
frame_pool = Direct3D11CaptureFramePool.create_free_threaded(
media_capture.media_capture_settings.direct3_d11_device,
get_direct3d_device(),
DirectXPixelFormat.B8_G8_R8_A8_UINT_NORMALIZED,
1,
item.size,
Expand Down
22 changes: 10 additions & 12 deletions src/capture_method/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,22 @@
from typing import TYPE_CHECKING, TypedDict

from pygrabber import dshow_graph
from winsdk.windows.media.capture import MediaCapture

from capture_method.BitBltCaptureMethod import BitBltCaptureMethod
from capture_method.CaptureMethodBase import CaptureMethodBase
from capture_method.DesktopDuplicationCaptureMethod import DesktopDuplicationCaptureMethod
from capture_method.ForceFullContentRenderingCaptureMethod import ForceFullContentRenderingCaptureMethod
from capture_method.VideoCaptureDeviceCaptureMethod import VideoCaptureDeviceCaptureMethod
from capture_method.WindowsGraphicsCaptureMethod import WindowsGraphicsCaptureMethod
from utils import WINDOWS_BUILD_NUMBER
from utils import WINDOWS_BUILD_NUMBER, get_direct3d_device

if TYPE_CHECKING:
from AutoSplit import AutoSplit

WGC_MIN_BUILD = 17134
"""https://docs.microsoft.com/en-us/uwp/api/windows.graphics.capture.graphicscapturepicker#applies-to"""
LEARNING_MODE_DEVICE_BUILD = 17763
"""https://learn.microsoft.com/en-us/uwp/api/windows.ai.machinelearning.learningmodeldevice"""


class Region(TypedDict):
Expand Down Expand Up @@ -121,8 +122,8 @@ def __getitem__(self, key: CaptureMethodEnum):
short_description="fast, most compatible, capped at 60fps",
description=(
f"\nOnly available in Windows 10.0.{WGC_MIN_BUILD} and up. "
"\nDue to current technical limitations, it requires having at least one "
"\naudio or video Capture Device connected and enabled. Even if it won't be used. "
f"\nDue to current technical limitations, Windows versions below 10.0.0.{LEARNING_MODE_DEVICE_BUILD}"
"\nrequire having at least one audio or video Capture Device connected and enabled."
"\nAllows recording UWP apps, Hardware Accelerated and Exclusive Fullscreen windows. "
"\nAdds a yellow border on Windows 10 (not on Windows 11)."
"\nCaps at around 60 FPS. "
Expand Down Expand Up @@ -166,21 +167,18 @@ def __getitem__(self, key: CaptureMethodEnum):
})


def test_for_media_capture():
async def coroutine():
return await (MediaCapture().initialize_async() or asyncio.sleep(0))
def try_get_direct3d_device():
try:
asyncio.run(coroutine())
return True
return get_direct3d_device()
except OSError:
return False
return None


# Detect and remove unsupported capture methods
if ( # Windows Graphics Capture requires a minimum Windows Build
WINDOWS_BUILD_NUMBER < WGC_MIN_BUILD
# Our current implementation of Windows Graphics Capture requires at least one CaptureDevice
or not test_for_media_capture()
# Our current implementation of Windows Graphics Capture does not ensure we can get an ID3DDevice
or not try_get_direct3d_device()
):
CAPTURE_METHODS.pop(CaptureMethodEnum.WINDOWS_GRAPHICS_CAPTURE)

Expand Down
19 changes: 19 additions & 0 deletions src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

import cv2
from win32 import win32gui
from winsdk.windows.ai.machinelearning import LearningModelDevice, LearningModelDeviceKind
from winsdk.windows.media.capture import MediaCapture

from gen.build_vars import AUTOSPLIT_BUILD_NUMBER, AUTOSPLIT_GITHUB_REPOSITORY

Expand Down Expand Up @@ -76,6 +78,23 @@ def get_window_bounds(hwnd: int) -> tuple[int, int, int, int]:
return window_left_bounds, window_top_bounds, window_width, window_height


def get_direct3d_device():
direct_3d_device = LearningModelDevice(LearningModelDeviceKind.DIRECT_X_HIGH_PERFORMANCE).direct3_d11_device
if not direct_3d_device:
# Note: Must create in the same thread (can't use a global) otherwise when ran from LiveSplit it will raise:
# OSError: The application called an interface that was marshalled for a different thread
media_capture = MediaCapture()

async def coroutine():
await (media_capture.initialize_async() or asyncio.sleep(0))
asyncio.run(coroutine())
direct_3d_device = media_capture.media_capture_settings and \
media_capture.media_capture_settings.direct3_d11_device
if not direct_3d_device:
raise OSError("Unable to initialize a Direct3D Device.")
return direct_3d_device


def fire_and_forget(func: Callable[..., Any]):
"""
Runs synchronous function asynchronously without waiting for a response
Expand Down

0 comments on commit 220b77f

Please sign in to comment.