Skip to content

Fix D3DDD issue on hybrid GPU laptops #205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions D3DDD-Note-Laptops.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Installation Note: D3D Desktop Duplication on Laptops

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.

## Problem

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)

## Solution

The solution is presented as such:

> Run the application on the integrated GPU instead of on the discrete GPU

Therefore, to be able to use _D3D Desktop Duplication_ on hybrid GPU laptops, we need to force Python to run on the integrated GPU.

## Approach 1: Windows 10 Settings

_You must be running Windows 10 1809 or later for this to work._

1. Press the Windows Key, type `Graphics settings` and press enter
2. You should see the following window:
![image](https://user-images.githubusercontent.com/35039/84433008-a3b65d00-abfb-11ea-8343-81b8f265afc4.png)
3. Make sure the dropdown is set to `Desktop App` and click `Browse`
4. Find the `python.exe` used by your _D3D Desktop Duplication_ project. Example:
![image](https://user-images.githubusercontent.com/35039/84433419-3d7e0a00-abfc-11ea-99a4-b5176535b0e5.png)
5. Click on `Options`
6. Select `Power saving` and click `Save`
![image](https://user-images.githubusercontent.com/35039/84433562-7918d400-abfc-11ea-807a-e3c0b15d9fb2.png)
7. If you did everything right it should look like this:
![image](https://user-images.githubusercontent.com/35039/84433706-bda46f80-abfc-11ea-9c64-a702b96095b8.png)
8. Repeat the process for other potentially relevant executables for your project: `ipython.exe`, `jupyter-kernel.exe` etc.

## Approach 2: Nvidia Control Panel

Need help to fill in this section. See issue [SerpentAI/D3DShot#27](https://github.com/SerpentAI/D3DShot/issues/27)

## Approach 3: AMD Catalyst Control Center

Need help to fill in this section. See issue [SerpentAI/D3DShot#28](https://github.com/SerpentAI/D3DShot/issues/28)

## Question: Won't this impede on my ability to use CUDA, OpenCL etc?

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.

---
(copied and adapted from <https://github.com/SerpentAI/D3DShot/wiki/Installation-Note:-Laptops>)
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ Refer to the [build instructions](build%20instructions.md) if you'd like to buil
It can record OpenGL and Hardware Accelerated windows.
About 10-15x slower than BitBlt. Not affected by window size.
overlapping windows will show up and can't record across displays.
This option may not be available for hybrid GPU laptops, see [D3DDD-Note-Laptops.md](/D3DDD-Note-Laptops.md) for a solution.
- **Force Full Content Rendering** (very slow, can affect rendering)
Uses BitBlt behind the scene, but passes a special flag to PrintWindow to force rendering the entire desktop.
About 10-15x slower than BitBlt based on original window size and can mess up some applications' rendering pipelines.
Expand Down
17 changes: 10 additions & 7 deletions src/capture_method/DesktopDuplicationCaptureMethod.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@
if TYPE_CHECKING:
from AutoSplit import AutoSplit

desktop_duplication = d3dshot.create(capture_output="numpy")


class DesktopDuplicationCaptureMethod(BitBltCaptureMethod): # pylint: disable=too-few-public-methods
def __init__(self):
super().__init__()
# Must not set statically as some laptops will throw an error
self.desktop_duplication = d3dshot.create(capture_output="numpy")

def get_frame(self, autosplit: AutoSplit):
selection = autosplit.settings_dict["capture_region"]
hwnd = autosplit.hwnd
Expand All @@ -26,19 +29,19 @@ def get_frame(self, autosplit: AutoSplit):
return None, False

left_bounds, top_bounds, *_ = get_window_bounds(hwnd)
desktop_duplication.display = [
self.desktop_duplication.display = [
display for display
in desktop_duplication.displays
in self.desktop_duplication.displays
if display.hmonitor == hmonitor
][0]
offset_x, offset_y, *_ = win32gui.GetWindowRect(hwnd)
offset_x -= desktop_duplication.display.position["left"]
offset_y -= desktop_duplication.display.position["top"]
offset_x -= self.desktop_duplication.display.position["left"]
offset_y -= self.desktop_duplication.display.position["top"]
left = selection["x"] + offset_x + left_bounds
top = selection["y"] + offset_y + top_bounds
right = selection["width"] + left
bottom = selection["height"] + top
screenshot = desktop_duplication.screenshot((left, top, right, bottom))
screenshot = self.desktop_duplication.screenshot((left, top, right, bottom))
if screenshot is None:
return None, False
return cv2.cvtColor(cast(cv2.Mat, screenshot), cv2.COLOR_RGBA2BGRA), False
39 changes: 24 additions & 15 deletions src/capture_method/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from enum import Enum, EnumMeta, unique
from typing import TYPE_CHECKING, TypedDict, cast

from _ctypes import COMError # pylint: disable=C2701
from pygrabber.dshow_graph import FilterGraph

from capture_method.BitBltCaptureMethod import BitBltCaptureMethod
Expand All @@ -14,7 +15,7 @@
from capture_method.ForceFullContentRenderingCaptureMethod import ForceFullContentRenderingCaptureMethod
from capture_method.VideoCaptureDeviceCaptureMethod import VideoCaptureDeviceCaptureMethod
from capture_method.WindowsGraphicsCaptureMethod import WindowsGraphicsCaptureMethod
from utils import WINDOWS_BUILD_NUMBER, first, try_get_direct3d_device
from utils import GITHUB_REPOSITORY, WINDOWS_BUILD_NUMBER, first, try_get_direct3d_device

if TYPE_CHECKING:
from AutoSplit import AutoSplit
Expand Down Expand Up @@ -44,11 +45,10 @@ class CaptureMethodMeta(EnumMeta):
# Allow checking if simple string is enum
def __contains__(self, other: str):
try:
self(other) # pyright: ignore [reportGeneralTypeIssues] pylint: disable=no-value-for-parameter
self(other) # pylint: disable=no-value-for-parameter
except ValueError:
return False
else:
return True
return True


@unique
Expand Down Expand Up @@ -147,17 +147,26 @@ def get(self, __key: CaptureMethodEnum):

implementation=BitBltCaptureMethod,
)
CAPTURE_METHODS[CaptureMethodEnum.DESKTOP_DUPLICATION] = CaptureMethodInfo(
name="Direct3D Desktop Duplication",
short_description="slower, bound to display",
description=(
"\nDuplicates the desktop using Direct3D. "
"\nIt can record OpenGL and Hardware Accelerated windows. "
"\nAbout 10-15x slower than BitBlt. Not affected by window size. "
"\nOverlapping windows will show up and can't record across displays. "
),
implementation=DesktopDuplicationCaptureMethod,
)
try:
import d3dshot
d3dshot.create(capture_output="numpy")
except (ModuleNotFoundError, COMError):
pass
else:
CAPTURE_METHODS[CaptureMethodEnum.DESKTOP_DUPLICATION] = CaptureMethodInfo(
name="Direct3D Desktop Duplication",
short_description="slower, bound to display",
description=(
"\nDuplicates the desktop using Direct3D. "
"\nIt can record OpenGL and Hardware Accelerated windows. "
"\nAbout 10-15x slower than BitBlt. Not affected by window size. "
"\nOverlapping windows will show up and can't record across displays. "
"\nThis option may not be available for hybrid GPU laptops, "
"\nsee D3DDD-Note-Laptops.md for a solution. "
f"\nhttps://www.github.com/{GITHUB_REPOSITORY}#capture-method "
),
implementation=DesktopDuplicationCaptureMethod,
)
CAPTURE_METHODS[CaptureMethodEnum.PRINTWINDOW_RENDERFULLCONTENT] = CaptureMethodInfo(
name="Force Full Content Rendering",
short_description="very slow, can affect rendering",
Expand Down