Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
178 / 33 / 17
Регистрация: 02.02.2014
Сообщений: 373

Захват рабочего стола в HDR

01.03.2022, 15:39. Показов 532. Ответов 0
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Привет всем!

Имеется комп с Windows 11, видеокарта дискретная, RTX 3080Ti, на рабочем столе включен HDR, глубина цвета 10 бит. Разрешение рабочего стола большое - 11520х2160. Я пытаюсь с помощью DirectX захватывать картинку с рабочего стола, уменьшать её в 400 раз (20 раз по ширине и 20 по высоте), и затем копировать в ОЗУ для дальнейшей обработки. В SDR всё работает, в HDR - тоже. Но, поскольку я захватываю в формате RGB 8bit, т.е. без HDR, то DirectX на лету автоматически конвертирует мне изображение рабочего стола из HDR в SDR. Проблема в том, что он делает это сразу при захвате, и поэтому захват рабочего стола отжирает 15% видеокарты (!).

Я хочу оптимизировать код: сначала захватывать рабочий стол в исходном формате без конвертации (т.е. 10 бит на канал), затем в этом же формате уменьшать в 400 раз, и уже после этого конвертировать HDR в SDR, чтобы сэкономить ресурсы GPU.

Вот код захвата рабочего стола:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
   CaptureResult captureDesktop()
        {
            var result = new CaptureResult();
 
 
            if (dxgiDevice == null)
            {
 
                dxgiDevice = _device.QueryInterface<DXGI.Device>();
                d2dFactory = new D2D.Factory1();
                d2dDevice = new D2D.Device(d2dFactory, dxgiDevice);
                frameDc = new D2D.DeviceContext(d2dDevice, D2D.DeviceContextOptions.None);
 
            }
            {
                // acquire frame
                var dresult = _outputDuplication.TryAcquireNextFrame(10000, out var finfo, out var frame);
                if (dresult.Success)
                    try
                    {
 
 
 
                        //using (frame)
 
                        // get DXGI surface/bitmap from resource
                        //using (D2D.DeviceContext? )
                        using (DXGI.Surface? frameSurface = frame.QueryInterface<DXGI.Surface>())
                        using (D2D.Bitmap1? frameBitmap = new D2D.Bitmap1(frameDc, frameSurface))
                        {
 
                            int w = frameSurface.Description.Width / DownscaleFactor;
                            int h = frameSurface.Description.Height / DownscaleFactor;
                            if (texture != null)
                            {
                                if (texture.Description.Width != w || texture.Description.Height != h)
                                {
                                    try { Interlocked.Exchange(ref texture, null)?.Dispose(); } catch { }
                                    try { Interlocked.Exchange(ref textureDc, null)?.Dispose(); } catch { }
                                    try { Interlocked.Exchange(ref textureSurface, null)?.Dispose(); } catch { }
                                    try { Interlocked.Exchange(ref textureBitmap, null)?.Dispose(); } catch { }
                                    texture = null;
                                }
                            }
                            if (texture == null)
                            {
                                var desc = new D3D11.Texture2DDescription
                                {
                                    CpuAccessFlags = D3D11.CpuAccessFlags.None, // only GPU
                                    BindFlags = D3D11.BindFlags.RenderTarget, // to use D2D
 
                                    
                                    
                                    
                                    Format = DXGI.Format.B8G8R8A8_UNorm, //для SDR
 
                                    
                                    
                                    
 
                                    
                                    Width = w,
                                    Height = h,
                                    OptionFlags = D3D11.ResourceOptionFlags.None,
                                    MipLevels = 1,
                                    ArraySize = 1,
                                    SampleDescription = { Count = 1, Quality = 0 },
                                    Usage = D3D11.ResourceUsage.Default
                                };
 
 
                                texture = new D3D11.Texture2D(_device, desc);
                                textureDc = new D2D.DeviceContext(d2dDevice, D2D.DeviceContextOptions.None);
                                textureSurface = texture.QueryInterface<DXGI.Surface>();
                                textureBitmap = new D2D.Bitmap1(textureDc, textureSurface);
                            }
 
                            // create a GPU resized texture/surface/bitmap
 
                            //using (D3D11.Texture2D? texture = new D3D11.Texture2D(_device, desc))
                            // using (D2D.DeviceContext? textureDc = new D2D.DeviceContext(d2dDevice, D2D.DeviceContextOptions.None)) // create a D2D device context
                            // using (DXGI.Surface? textureSurface = texture.QueryInterface<DXGI.Surface>()) // this texture is a DXGI surface
                            // using (D2D.Bitmap1? textureBitmap = new D2D.Bitmap1(textureDc, textureSurface)) // we can create a GPU bitmap on a DXGI surface
 
                            // associate the DC with the GPU texture/surface/bitmap
                            textureDc.Target = textureBitmap;
 
                            // this is were we draw on the GPU texture/surface
                            textureDc.BeginDraw();
 
                            // this will automatically resize
                            textureDc.DrawBitmap(
                                frameBitmap,
                                new Interop.RawRectangleF(0, 0, w, h),
                                1,
                                D2D.InterpolationMode.HighQualityCubic, // change this for quality vs speed
                                null,
                                null);
 
                            // commit draw
                            textureDc.EndDraw();
 
 
                            if (cpuBitmap == null || (int)cpuBitmap.Size.Width != w || (int)cpuBitmap.Size.Height != h)
                            {
                                if (cpuBitmap != null)
                                {
                                    try { cpuBitmap.Unmap(); } catch { }
                                    try { cpuBitmap.Dispose(); } catch { }
                                }
                                var bitmapProperties = new D2D.BitmapProperties1();
                                bitmapProperties.BitmapOptions = BitmapOptions.CannotDraw | BitmapOptions.CpuRead;
                                bitmapProperties.PixelFormat = textureBitmap.PixelFormat;
                                cpuBitmap = new Bitmap1(frameDc, new SharpDX.Size2(w, h), bitmapProperties);
                                mapOfCpuBitmap = cpuBitmap.Map(MapOptions.Read);
                            }
 
                            cpuBitmap.CopyFromBitmap(textureBitmap);
                            var cpuBuffer = getCpuBuffer(w, h);
                            unsafe
                            {
                                var cpuBufferBits = cpuBuffer.LockBits(new Rectangle(0, 0, cpuBuffer.Width, cpuBuffer.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                                try
                                {
                                    var byteCount = cpuBuffer.Width * cpuBuffer.Height * 4;
                                    Buffer.MemoryCopy(mapOfCpuBitmap.DataPointer.ToPointer(), cpuBufferBits.Scan0.ToPointer(), byteCount, byteCount);
                                }
                                finally
                                {
                                    cpuBuffer.UnlockBits(cpuBufferBits);
                                }
                            }
                            return new CaptureResult
                            {
                                Success = true,
                                Buffer = cpuBuffer,
                                Exception = null
                            };
                        }
 
                    }
                    finally
                    {
                        _outputDuplication.ReleaseFrame();
                    }
                else
                {
                    recreateResources();
                }
            }
 
            return result;
        }
Здесь говорится, что при работе с HDR я должен использовать буфер DXGI_FORMAT_R16G16B16A16_FLOAT. Поставил такой формат - заработало:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 var desc = new D3D11.Texture2DDescription
                                {
                                    CpuAccessFlags = D3D11.CpuAccessFlags.None, // only GPU
                                    BindFlags = D3D11.BindFlags.RenderTarget, // to use D2D
 
 
                                    Format = DXGI.Format.R16G16B16A16_Float,
 
 
                                    //Format = DXGI.Format.R10G10B10A2_UNorm, не работает :(
                                    Width = w,
                                    Height = h,
                                    OptionFlags = D3D11.ResourceOptionFlags.None,
                                    MipLevels = 1,
                                    ArraySize = 1,
                                    SampleDescription = { Count = 1, Quality = 0 },
                                    Usage = D3D11.ResourceUsage.Default
                                };
 
 
                                texture = new D3D11.Texture2D(_device, desc);
                                textureDc = new D2D.DeviceContext(d2dDevice, D2D.DeviceContextOptions.None);
                                textureSurface = texture.QueryInterface<DXGI.Surface>();
                                textureBitmap = new D2D.Bitmap1(textureDc, textureSurface);

Однако, при копировании уменьшенного результата в ОЗУ возникает проблема: если рассчитывать размер буфера в ОЗУ как 8 * ширина * высота, то всё падает с ошибкой.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  if (cpuBitmap == null || (int)cpuBitmap.Size.Width != w || (int)cpuBitmap.Size.Height != h)
                            {
                                if (cpuBitmap != null)
                                {
                                    try { cpuBitmap.Unmap(); } catch { }
                                    try { cpuBitmap.Dispose(); } catch { }
                                }
                                var bitmapProperties = new D2D.BitmapProperties1();
                                bitmapProperties.BitmapOptions = BitmapOptions.CannotDraw | BitmapOptions.CpuRead;
                                bitmapProperties.PixelFormat = textureBitmap.PixelFormat;
                                cpuBitmap = new Bitmap1(frameDc, new SharpDX.Size2(w, h), bitmapProperties);
                                mapOfCpuBitmap = cpuBitmap.Map(MapOptions.Read);
                            }
 
                            cpuBitmap.CopyFromBitmap(textureBitmap); //Видимо, это не работает :(
                            var cpuBuffer = getCpuBuffer(w, h, canvasTextureDescription.Format);
                            unsafe
                            {
                                var cpuBufferBits = cpuBuffer.LockBits(new Rectangle(0, 0, cpuBuffer.Width, cpuBuffer.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                                try
                                {
                                    int bytesPerPixel = getBytesPerPixelCount(canvasTextureDescription.Format);
                                    //bytesPerPixel = 8; Не работает, падает
                                    //bytesPerPixel = 4; Работает, но копируются нули :(
                                    var byteCount = cpuBuffer.Width * cpuBuffer.Height * bytesPerPixel;
                                    Buffer.MemoryCopy(mapOfCpuBitmap.DataPointer.ToPointer(), cpuBufferBits.Scan0.ToPointer(), byteCount, byteCount);
                                }
                                finally
                                {
                                    cpuBuffer.UnlockBits(cpuBufferBits);
                                }
                            }
Если же рассчитывать размер копируемого буфера как 4 * ширина * высота, копирование происходит, но при этом копируются нули. Функция, создающая буфер в ОЗУ:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 System.Drawing.Bitmap getCpuBuffer(int w, int h, DXGI.Format gpuPixelFormat)
        {
            bool r = false;
            if (cpuBuffer == null)
                r = true;
            else if (cpuBuffer.Width != w || cpuBuffer.Height != h)
                r = true;
 
            System.Drawing.Imaging.PixelFormat cpuPixelFormat = default;
 
            if (gpuPixelFormat == DXGI.Format.B8G8R8A8_UNorm)
                cpuPixelFormat = System.Drawing.Imaging.PixelFormat.Format32bppArgb;
            else if (gpuPixelFormat == DXGI.Format.R16G16B16A16_Float)
                cpuPixelFormat = System.Drawing.Imaging.PixelFormat.Format64bppArgb;
            else
                throw new NotSupportedException();
 
            if (r)
                Interlocked.Exchange(ref cpuBuffer, new System.Drawing.Bitmap(w, h, cpuPixelFormat))?.Dispose();
            return cpuBuffer;
        }
Т.е. формат цвета я указываю правильный - Format64bppArgb - по крайней мере, памяти выделяется ровно столько, сколько нужно.

Я посмотрел в окне "Память" содержимое cpuBitmap - там нули. Т.е., как я понимаю, здесь либо cpuBitmap.CopyFromBitmap(textureBitmap); не копирует данные из видеокарты в ОЗУ, либо в видеокарте данные изначально нули. Но тогда возникает ещё один вопрос - почему при копировании точно рассчитанного числа байт оно падает? Как будто один из буферов меньше рассчитанного размера.

Помогите, пожалуйста, разобраться с этим. Мне осталось только скопировать данные из видеокарты в ОЗУ.

Добавлено через 13 минут
Нашёл косяк почему падало: вместо
C#
1
var cpuBufferBits = cpuBuffer.LockBits(new Rectangle(0, 0, cpuBuffer.Width, cpuBuffer.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
нужно было указать
C#
1
var cpuBufferBits = cpuBuffer.LockBits(new Rectangle(0, 0, cpuBuffer.Width, cpuBuffer.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, cpuBuffer.PixelFormat);
но копируются по-прежнему нули, если посмотреть память по адресу mapOfCpuBitmap.DataPointer - там нули.
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
01.03.2022, 15:39
Ответы с готовыми решениями:

Быстрый захват рабочего стола
Привет всем! Есть довольно тривиальная задача - быстро (не менее 30 к/с) захватывать рабочий стол. Обсуждение было в другой ветке, решил...

Захват видео с рабочего стола
Задача такая: есть флеш-игра (летит смолетик, преодолевает препятствия; управление только вверх/вниз) нужно захватить видео с экрана,...

FFmpeg захват рабочего стола
Добрый вечер! Может плохо искал, но все же, обращаюсь к вам. Подскажите средство для захвата изображения с рабочего стола, которое можно...

0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
01.03.2022, 15:39
Помогаю со студенческими работами здесь

Scroll рабочего стола - Эффект отображения иконок рабочего стола
Ещё один пример скроллинга рабочего стола, изменения стиля отображения, т.е. большие иконки или же репорт стиль.

2 рабочего стола
В общем купил кабель HDMI - DVI-D Воткнул к ноуту HP G62 b062er и к монитору LG M1740a-RZ Видеокарта ATI Mobillity HD5470 Расширил...

Обновление рабочего стола
Всем привет! Встал вопрос после создания/удаления ярлыков на рабочем столе обновить последний. Все что нашел на форуме, так это...

Блокировка рабочего стола!
Доброго времени суток, вчера стало интересно что такое с++ можете помочь? цель такая:HWND des=GetDesktopWindow(); ...

Демонстрация рабочего стола
Здравствуйте, уважаемые. Как во флексе получить видео происходящего на экране? С меня тут требуют нечто вроде скайпа с минимумом...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
1
Ответ Создать тему
Новые блоги и статьи
Оттенки серого
Argus19 18.03.2026
Оттенки серого Нашёл в интернете 3 прекрасных модуля: Модуль класса открытия диалога открытия/ сохранения файла на Win32 API; Модуль класса быстрого перекодирования цветного изображения в оттенки. . .
SDL3 для Desktop (MinGW): Рисуем цветные прямоугольники с помощью рисовальщика SDL3 на Си и C++
8Observer8 17.03.2026
Содержание блога Финальные проекты на Си и на C++: finish-rectangles-sdl3-c. zip finish-rectangles-sdl3-cpp. zip
Символические и жёсткие ссылки в Linux.
algri14 15.03.2026
Существует два типа ссылок — символические и жёсткие. Ссылка в Linux — это запись в каталоге, которая может указывать либо на inode «файла-ИСТОЧНИКА», тогда это будет «жёсткая ссылка» (hard link),. . .
[Owen Logic] Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора
ФедосеевПавел 14.03.2026
Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора ВВЕДЕНИЕ Выполняя задание на управление насосной группой заполнения резервуара,. . .
делаю науч статью по влиянию грибов на сукцессию
anaschu 13.03.2026
прикрепляю статью
SDL3 для Desktop (MinGW): Создаём пустое окно с нуля для 2D-графики на SDL3, Си и C++
8Observer8 10.03.2026
Содержание блога Финальные проекты на Си и на C++: hello-sdl3-c. zip hello-sdl3-cpp. zip Результат:
Установка CMake и MinGW 13.1 для сборки С и C++ приложений из консоли и из Qt Creator в EXE
8Observer8 10.03.2026
Содержание блога MinGW - это коллекция инструментов для сборки приложений в EXE. CMake - это система сборки приложений. Здесь описаны базовые шаги для старта программирования с помощью CMake и. . .
Как дизайн сайта влияет на конверсию: 7 решений, которые реально повышают заявки
Neotwalker 08.03.2026
Многие до сих пор воспринимают дизайн сайта как “красивую оболочку”. На практике всё иначе: дизайн напрямую влияет на то, оставит человек заявку или уйдёт через несколько секунд. Даже если у вас. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru