Skip to content

Commit 19adb71

Browse files
authored
Fix D3DDD issue on hybrid GPU laptops (#205)
* Fix D3DDD issue on hybrid gpu laptops 1. Some Desktop Duplication setup was done in advance, even if that method won't be used 2. Hybrid GPU laptops can have issues with D3D Desktop Duplication. The issue and a workaround is now documented * Fixed some blocking typing and linting issues
1 parent 429fe96 commit 19adb71

File tree

4 files changed

+82
-22
lines changed

4 files changed

+82
-22
lines changed

D3DDD-Note-Laptops.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Installation Note: D3D Desktop Duplication on Laptops
2+
3+
Windows has a little quirk when running Desktop Duplication on laptops with hybrid GPU systems (integrated + dedicated). You will need to perform an additional tweak to get _D3D Desktop Duplication_ to work correctly on your system.
4+
5+
## Problem
6+
7+
The problem is fully documented in [this article](https://support.microsoft.com/en-us/help/3019314/error-generated-when-desktop-duplication-api-capable-application-is-ru)
8+
9+
## Solution
10+
11+
The solution is presented as such:
12+
13+
> Run the application on the integrated GPU instead of on the discrete GPU
14+
15+
Therefore, to be able to use _D3D Desktop Duplication_ on hybrid GPU laptops, we need to force Python to run on the integrated GPU.
16+
17+
## Approach 1: Windows 10 Settings
18+
19+
_You must be running Windows 10 1809 or later for this to work._
20+
21+
1. Press the Windows Key, type `Graphics settings` and press enter
22+
2. You should see the following window:
23+
![image](https://user-images.githubusercontent.com/35039/84433008-a3b65d00-abfb-11ea-8343-81b8f265afc4.png)
24+
3. Make sure the dropdown is set to `Desktop App` and click `Browse`
25+
4. Find the `python.exe` used by your _D3D Desktop Duplication_ project. Example:
26+
![image](https://user-images.githubusercontent.com/35039/84433419-3d7e0a00-abfc-11ea-99a4-b5176535b0e5.png)
27+
5. Click on `Options`
28+
6. Select `Power saving` and click `Save`
29+
![image](https://user-images.githubusercontent.com/35039/84433562-7918d400-abfc-11ea-807a-e3c0b15d9fb2.png)
30+
7. If you did everything right it should look like this:
31+
![image](https://user-images.githubusercontent.com/35039/84433706-bda46f80-abfc-11ea-9c64-a702b96095b8.png)
32+
8. Repeat the process for other potentially relevant executables for your project: `ipython.exe`, `jupyter-kernel.exe` etc.
33+
34+
## Approach 2: Nvidia Control Panel
35+
36+
Need help to fill in this section. See issue [SerpentAI/D3DShot#27](https://github.com/SerpentAI/D3DShot/issues/27)
37+
38+
## Approach 3: AMD Catalyst Control Center
39+
40+
Need help to fill in this section. See issue [SerpentAI/D3DShot#28](https://github.com/SerpentAI/D3DShot/issues/28)
41+
42+
## Question: Won't this impede on my ability to use CUDA, OpenCL etc?
43+
44+
Preliminary answer: No. This is telling Windows how to _render_ Python processes with the Desktop Window Manager. Most Python applications are console applications that don't have a window. Even if you have a GUI application with one or more windows, this should only affect the rendering aspect (i.e. your windows won't be rendered through the dedicated GPU) and shouldn't limit hardware access in any way.
45+
46+
---
47+
(copied and adapted from <https://github.com/SerpentAI/D3DShot/wiki/Installation-Note:-Laptops>)

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ Refer to the [build instructions](build%20instructions.md) if you'd like to buil
8484
It can record OpenGL and Hardware Accelerated windows.
8585
About 10-15x slower than BitBlt. Not affected by window size.
8686
overlapping windows will show up and can't record across displays.
87+
This option may not be available for hybrid GPU laptops, see [D3DDD-Note-Laptops.md](/D3DDD-Note-Laptops.md) for a solution.
8788
- **Force Full Content Rendering** (very slow, can affect rendering)
8889
Uses BitBlt behind the scene, but passes a special flag to PrintWindow to force rendering the entire desktop.
8990
About 10-15x slower than BitBlt based on original window size and can mess up some applications' rendering pipelines.

src/capture_method/DesktopDuplicationCaptureMethod.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@
1414
if TYPE_CHECKING:
1515
from AutoSplit import AutoSplit
1616

17-
desktop_duplication = d3dshot.create(capture_output="numpy")
18-
1917

2018
class DesktopDuplicationCaptureMethod(BitBltCaptureMethod): # pylint: disable=too-few-public-methods
19+
def __init__(self):
20+
super().__init__()
21+
# Must not set statically as some laptops will throw an error
22+
self.desktop_duplication = d3dshot.create(capture_output="numpy")
23+
2124
def get_frame(self, autosplit: AutoSplit):
2225
selection = autosplit.settings_dict["capture_region"]
2326
hwnd = autosplit.hwnd
@@ -26,19 +29,19 @@ def get_frame(self, autosplit: AutoSplit):
2629
return None, False
2730

2831
left_bounds, top_bounds, *_ = get_window_bounds(hwnd)
29-
desktop_duplication.display = [
32+
self.desktop_duplication.display = [
3033
display for display
31-
in desktop_duplication.displays
34+
in self.desktop_duplication.displays
3235
if display.hmonitor == hmonitor
3336
][0]
3437
offset_x, offset_y, *_ = win32gui.GetWindowRect(hwnd)
35-
offset_x -= desktop_duplication.display.position["left"]
36-
offset_y -= desktop_duplication.display.position["top"]
38+
offset_x -= self.desktop_duplication.display.position["left"]
39+
offset_y -= self.desktop_duplication.display.position["top"]
3740
left = selection["x"] + offset_x + left_bounds
3841
top = selection["y"] + offset_y + top_bounds
3942
right = selection["width"] + left
4043
bottom = selection["height"] + top
41-
screenshot = desktop_duplication.screenshot((left, top, right, bottom))
44+
screenshot = self.desktop_duplication.screenshot((left, top, right, bottom))
4245
if screenshot is None:
4346
return None, False
4447
return cv2.cvtColor(cast(cv2.Mat, screenshot), cv2.COLOR_RGBA2BGRA), False

src/capture_method/__init__.py

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from enum import Enum, EnumMeta, unique
77
from typing import TYPE_CHECKING, TypedDict, cast
88

9+
from _ctypes import COMError # pylint: disable=C2701
910
from pygrabber.dshow_graph import FilterGraph
1011

1112
from capture_method.BitBltCaptureMethod import BitBltCaptureMethod
@@ -14,7 +15,7 @@
1415
from capture_method.ForceFullContentRenderingCaptureMethod import ForceFullContentRenderingCaptureMethod
1516
from capture_method.VideoCaptureDeviceCaptureMethod import VideoCaptureDeviceCaptureMethod
1617
from capture_method.WindowsGraphicsCaptureMethod import WindowsGraphicsCaptureMethod
17-
from utils import WINDOWS_BUILD_NUMBER, first, try_get_direct3d_device
18+
from utils import GITHUB_REPOSITORY, WINDOWS_BUILD_NUMBER, first, try_get_direct3d_device
1819

1920
if TYPE_CHECKING:
2021
from AutoSplit import AutoSplit
@@ -44,11 +45,10 @@ class CaptureMethodMeta(EnumMeta):
4445
# Allow checking if simple string is enum
4546
def __contains__(self, other: str):
4647
try:
47-
self(other) # pyright: ignore [reportGeneralTypeIssues] pylint: disable=no-value-for-parameter
48+
self(other) # pylint: disable=no-value-for-parameter
4849
except ValueError:
4950
return False
50-
else:
51-
return True
51+
return True
5252

5353

5454
@unique
@@ -147,17 +147,26 @@ def get(self, __key: CaptureMethodEnum):
147147

148148
implementation=BitBltCaptureMethod,
149149
)
150-
CAPTURE_METHODS[CaptureMethodEnum.DESKTOP_DUPLICATION] = CaptureMethodInfo(
151-
name="Direct3D Desktop Duplication",
152-
short_description="slower, bound to display",
153-
description=(
154-
"\nDuplicates the desktop using Direct3D. "
155-
"\nIt can record OpenGL and Hardware Accelerated windows. "
156-
"\nAbout 10-15x slower than BitBlt. Not affected by window size. "
157-
"\nOverlapping windows will show up and can't record across displays. "
158-
),
159-
implementation=DesktopDuplicationCaptureMethod,
160-
)
150+
try:
151+
import d3dshot
152+
d3dshot.create(capture_output="numpy")
153+
except (ModuleNotFoundError, COMError):
154+
pass
155+
else:
156+
CAPTURE_METHODS[CaptureMethodEnum.DESKTOP_DUPLICATION] = CaptureMethodInfo(
157+
name="Direct3D Desktop Duplication",
158+
short_description="slower, bound to display",
159+
description=(
160+
"\nDuplicates the desktop using Direct3D. "
161+
"\nIt can record OpenGL and Hardware Accelerated windows. "
162+
"\nAbout 10-15x slower than BitBlt. Not affected by window size. "
163+
"\nOverlapping windows will show up and can't record across displays. "
164+
"\nThis option may not be available for hybrid GPU laptops, "
165+
"\nsee D3DDD-Note-Laptops.md for a solution. "
166+
f"\nhttps://www.github.com/{GITHUB_REPOSITORY}#capture-method "
167+
),
168+
implementation=DesktopDuplicationCaptureMethod,
169+
)
161170
CAPTURE_METHODS[CaptureMethodEnum.PRINTWINDOW_RENDERFULLCONTENT] = CaptureMethodInfo(
162171
name="Force Full Content Rendering",
163172
short_description="very slow, can affect rendering",

0 commit comments

Comments
 (0)