OutputManager.h
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
#ifndef _OUTPUTMANAGER_H_
#define _OUTPUTMANAGER_H_
#pragma comment(lib, "D3D11")
#include <atlbase.h>
#include <DirectXMath.h>
#include <dxgi1_2.h>
#include <dcomp.h>
#include <wrl.h>
#include <new>
#include <windows.h>
#include <assert.h>
#include <wincodec.h>
#include <d3d11_1.h>
#define NUMVERTICES 6
#define BPP 4
#define OCCLUSION_STATUS_MSG WM_USER
extern HRESULT SystemTransitionsExpectedErrors[];
extern HRESULT CreateDuplicationExpectedErrors[];
extern HRESULT FrameInfoExpectedErrors[];
extern HRESULT AcquireFrameExpectedError[];
extern HRESULT EnumOutputsExpectedErrors[];
typedef _Return_type_success_(return == DUPL_RETURN_SUCCESS) enum
{
DUPL_RETURN_SUCCESS = 0,
DUPL_RETURN_ERROR_EXPECTED = 1,
DUPL_RETURN_ERROR_UNEXPECTED = 2
}DUPL_RETURN;
_Post_satisfies_(return != DUPL_RETURN_SUCCESS)
DUPL_RETURN ProcessFailure(_In_opt_ ID3D11Device* Device, _In_ LPCWSTR Str, _In_ LPCWSTR Title, HRESULT hr, _In_opt_z_ HRESULT* ExpectedErrors = nullptr);
void DisplayMsg(_In_ LPCWSTR Str, _In_ LPCWSTR Title, HRESULT hr);
//
// Handles the task of drawing into a window.
// Has the functionality to draw the mouse given a mouse shape buffer and position
//
class OUTPUTMANAGER
{
public:
OUTPUTMANAGER();
~OUTPUTMANAGER();
DUPL_RETURN CreateAccessibleSurf(RECT* DeskBounds, DXGI_FORMAT Format);
void CleanRefs();
HANDLE GetSharedHandle();
void WindowResize();
bool InitD3D11Video(HWND Window, int width, int height);
bool PresentVideoFrame(int width, int height);
// Vars
ID3D11Device* m_Device;
ID3D11Texture2D* m_SharedSurf;
ID3D11Texture2D* m_AccessibleSurf;
ID3D11DeviceContext* m_DeviceContext;
IDXGISwapChain1* m_SwapChain;
private:
// Methods
DUPL_RETURN CreateSharedSurf(RECT* DeskBounds);
DUPL_RETURN ResizeSwapChain();
bool CreateVideoProcessor(int width, int height, bool reset);
void RenderD3D11Texture(int width, int height);
ID3D10Multithread* p_mt = nullptr;
ID3D11VideoDevice* d3d11_video_device_ = nullptr;
ID3D11VideoContext* d3d11_video_context_ = nullptr;
CComPtr<ID3D11VideoProcessor> video_processor_;
CComPtr<ID3D11VideoProcessorEnumerator> video_processor_enum_;
// Vars
IDXGIFactory2* m_Factory;
HWND m_WindowHandle;
bool m_NeedsResize;
};
#endif
OutputManager.cpp
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
#include "OutputManager.h"
#include <iostream>
#include <array>
#include <cstdio>
using namespace DirectX;
float red = 0.0f;
// Below are lists of errors expect from Dxgi API calls when a transition event like mode change, PnpStop, PnpStart
// desktop switch, TDR or session disconnect/reconnect. In all these cases we want the application to clean up the threads that process
// the desktop updates and attempt to recreate them.
// If we get an error that is not on the appropriate list then we exit the application
// These are the errors we expect from general Dxgi API due to a transition
HRESULT SystemTransitionsExpectedErrors[] = {
DXGI_ERROR_DEVICE_REMOVED,
DXGI_ERROR_ACCESS_LOST,
static_cast<HRESULT>(WAIT_ABANDONED),
S_OK // Terminate list with zero valued HRESULT
};
// These are the errors we expect from IDXGIOutput1::DuplicateOutput due to a transition
HRESULT CreateDuplicationExpectedErrors[] = {
DXGI_ERROR_DEVICE_REMOVED,
static_cast<HRESULT>(E_ACCESSDENIED),
DXGI_ERROR_UNSUPPORTED,
DXGI_ERROR_SESSION_DISCONNECTED,
S_OK // Terminate list with zero valued HRESULT
};
// These are the errors we expect from IDXGIOutputDuplication methods due to a transition
HRESULT FrameInfoExpectedErrors[] = {
DXGI_ERROR_DEVICE_REMOVED,
DXGI_ERROR_ACCESS_LOST,
S_OK // Terminate list with zero valued HRESULT
};
// These are the errors we expect from IDXGIAdapter::EnumOutputs methods due to outputs becoming stale during a transition
HRESULT EnumOutputsExpectedErrors[] = {
DXGI_ERROR_NOT_FOUND,
S_OK // Terminate list with zero valued HRESULT
};
//
// Constructor NULLs out all pointers & sets appropriate var vals
//
OUTPUTMANAGER::OUTPUTMANAGER() : m_SwapChain(nullptr),
m_Device(nullptr),
m_Factory(nullptr),
m_DeviceContext(nullptr),
m_SharedSurf(nullptr),
m_WindowHandle(nullptr),
m_NeedsResize(false)
{
}
//
// Destructor which calls CleanRefs to release all references and memory.
//
OUTPUTMANAGER::~OUTPUTMANAGER()
{
CleanRefs();
}
//
// Indicates that window has been resized.
//
void OUTPUTMANAGER::WindowResize()
{
m_NeedsResize = true;
}
DUPL_RETURN OUTPUTMANAGER::CreateAccessibleSurf(_In_ RECT* DeskBounds, _In_ DXGI_FORMAT Format)
{
D3D11_TEXTURE2D_DESC desc;
desc.Width = DeskBounds->right - DeskBounds->left;
desc.Height = DeskBounds->bottom - DeskBounds->top;
desc.Format = Format;
desc.ArraySize = 1;
desc.BindFlags = 0;
desc.MiscFlags = 0;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.MipLevels = 1;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ;
desc.Usage = D3D11_USAGE_STAGING;
HRESULT hr = OUTPUTMANAGER::m_Device->CreateTexture2D(&desc, NULL, &m_AccessibleSurf);
if (FAILED(hr))
{
MessageBoxW(nullptr, L"Creating cpu accessable texture failed.", L"Error", MB_OK);
return DUPL_RETURN_ERROR_UNEXPECTED;
}
if (m_AccessibleSurf == nullptr)
{
MessageBoxW(nullptr, L"Creating cpu accessable texture failed.", L"Error", MB_OK);
return DUPL_RETURN_ERROR_UNEXPECTED;
}
return DUPL_RETURN_SUCCESS;
}
//
// Recreate shared texture
//
DUPL_RETURN OUTPUTMANAGER::CreateSharedSurf(_In_ RECT* DeskBounds)
{
HRESULT hr;
// Create shared texture for threads to draw into
D3D11_TEXTURE2D_DESC DeskTexD;
RtlZeroMemory(&DeskTexD, sizeof(D3D11_TEXTURE2D_DESC));
DeskTexD.Width = DeskBounds->right - DeskBounds->left;
DeskTexD.Height = DeskBounds->bottom - DeskBounds->top;
DeskTexD.MipLevels = 1;
DeskTexD.ArraySize = 1;
DeskTexD.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
DeskTexD.SampleDesc.Count = 1;
DeskTexD.Usage = D3D11_USAGE_DEFAULT;
DeskTexD.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
DeskTexD.CPUAccessFlags = 0;
hr = m_Device->CreateTexture2D(&DeskTexD, nullptr, &m_SharedSurf);
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to create shared texture", L"Error", hr, SystemTransitionsExpectedErrors);
}
return CreateAccessibleSurf(DeskBounds, DeskTexD.Format);
}
//
// Returns shared handle
//
HANDLE OUTPUTMANAGER::GetSharedHandle()
{
HANDLE Hnd = nullptr;
// QI IDXGIResource interface to synchronized shared surface.
IDXGIResource* DXGIResource = nullptr;
HRESULT hr = m_SharedSurf->QueryInterface(__uuidof(IDXGIResource), reinterpret_cast<void**>(&DXGIResource));
if (SUCCEEDED(hr))
{
// Obtain handle to IDXGIResource object.
DXGIResource->GetSharedHandle(&Hnd);
DXGIResource->Release();
DXGIResource = nullptr;
}
return Hnd;
}
//
// Resize swapchain
//
DUPL_RETURN OUTPUTMANAGER::ResizeSwapChain()
{
RECT WindowRect;
GetClientRect(m_WindowHandle, &WindowRect);
UINT Width = WindowRect.right - WindowRect.left;
UINT Height = WindowRect.bottom - WindowRect.top;
// Resize swapchain
DXGI_SWAP_CHAIN_DESC SwapChainDesc;
m_SwapChain->GetDesc(&SwapChainDesc);
HRESULT hr = m_SwapChain->ResizeBuffers(SwapChainDesc.BufferCount, Width, Height, SwapChainDesc.BufferDesc.Format, SwapChainDesc.Flags);
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to resize swapchain buffers in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors);
}
DUPL_RETURN Ret = DUPL_RETURN_SUCCESS;
return Ret;
}
//
// Releases all references
//
void OUTPUTMANAGER::CleanRefs()
{
if (m_DeviceContext)
{
m_DeviceContext->Release();
m_DeviceContext = nullptr;
}
if (m_Device)
{
m_Device->Release();
m_Device = nullptr;
}
if (m_SwapChain)
{
m_SwapChain->Release();
m_SwapChain = nullptr;
}
if (m_SharedSurf)
{
m_SharedSurf->Release();
m_SharedSurf = nullptr;
}
if (m_AccessibleSurf)
{
m_AccessibleSurf->Release();
m_AccessibleSurf = nullptr;
}
if (m_Factory)
{
m_Factory->Release();
m_Factory = nullptr;
}
}
bool OUTPUTMANAGER::InitD3D11Video(HWND Window, int width, int height)
{
HRESULT hr = S_OK;
UINT creation_flags = 0;
m_WindowHandle = Window;
D3D_FEATURE_LEVEL feature_levels_in[] = { D3D_FEATURE_LEVEL_9_1, D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_11_1 };
D3D_FEATURE_LEVEL feature_levels_out;
hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr,
creation_flags, feature_levels_in,
sizeof(feature_levels_in) / sizeof(D3D_FEATURE_LEVEL),
D3D11_SDK_VERSION, &m_Device,
&feature_levels_out, &m_DeviceContext);
if (FAILED(hr)) {
return false;
}
// Get video device
if (!d3d11_video_device_) {
hr = m_Device->QueryInterface(__uuidof(ID3D11VideoDevice),
(void**)&d3d11_video_device_);
if (FAILED(hr)) {
return false;
}
}
if (!d3d11_video_context_) {
hr = m_DeviceContext->QueryInterface(__uuidof(ID3D11VideoContext),
(void**)&d3d11_video_context_);
if (FAILED(hr)) {
return false;
}
}
// SwapChain
RECT WindowRect;
GetClientRect(m_WindowHandle, &WindowRect);
UINT window_width_ = WindowRect.right - WindowRect.left;
UINT window_height_ = WindowRect.bottom - WindowRect.top;
DXGI_SWAP_CHAIN_DESC1 desc;
ZeroMemory(&desc, sizeof(DXGI_SWAP_CHAIN_DESC1));
desc.BufferCount = 2;
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.Height = window_height_;
desc.Width = window_width_;
desc.Scaling = DXGI_SCALING_STRETCH;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
desc.Stereo = false;
desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
CComPtr<IDXGIDevice2> dxgi_device;
hr = m_Device->QueryInterface(__uuidof(IDXGIDevice1),
(void**)&dxgi_device);
if (FAILED(hr)) {
return false;
}
Microsoft::WRL::ComPtr<IDXGIAdapter> adapter = nullptr;
Microsoft::WRL::ComPtr<IDXGIFactory2> factory = nullptr;
hr = dxgi_device->GetAdapter(&adapter);
if (FAILED(hr)) {
return false;
}
hr = adapter->GetParent(IID_PPV_ARGS(&factory));
if (FAILED(hr)) {
return false;
}
hr = factory->CreateSwapChainForHwnd(m_Device, m_WindowHandle, &desc, nullptr, nullptr, &m_SwapChain);
if (FAILED(hr)) {
std::string message = std::system_category().message(hr);
return false;
}
// Create shared texture
RECT video = { 0 };
video.right = width;
video.bottom = height;
DUPL_RETURN Return = CreateSharedSurf(&video);
if (Return != DUPL_RETURN_SUCCESS)
{
return Return;
}
return true;
}
bool OUTPUTMANAGER::PresentVideoFrame(int width, int height)
{
if (!CreateVideoProcessor(width, height, false))
{
return false;
}
RenderD3D11Texture(width, height);
}
bool OUTPUTMANAGER::CreateVideoProcessor(int width,
int height,
bool reset)
{
HRESULT hr = S_OK;
if (width < 0 || height < 0)
return false;
D3D11_VIDEO_PROCESSOR_CONTENT_DESC content_desc;
ZeroMemory(&content_desc, sizeof(content_desc));
if (video_processor_.p && video_processor_enum_.p) {
hr = video_processor_enum_->GetVideoProcessorContentDesc(&content_desc);
if (FAILED(hr))
return false;
if (content_desc.InputWidth != (unsigned int)width ||
content_desc.InputHeight != (unsigned int)height ||
content_desc.OutputWidth != width ||
content_desc.OutputHeight != height || reset) {
video_processor_enum_.Release();
video_processor_.Release();
}
else {
return true;
}
}
ZeroMemory(&content_desc, sizeof(content_desc));
content_desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
content_desc.InputFrameRate.Numerator = 30;
content_desc.InputFrameRate.Denominator = 1;
content_desc.InputWidth = width;
content_desc.InputHeight = height;
content_desc.OutputWidth = width;
content_desc.OutputHeight = height;
content_desc.OutputFrameRate.Numerator = 30;
content_desc.OutputFrameRate.Denominator = 1;
content_desc.Usage = D3D11_VIDEO_USAGE_OPTIMAL_SPEED;
hr = d3d11_video_device_->CreateVideoProcessorEnumerator(&content_desc, &video_processor_enum_);
if (FAILED(hr))
return false;
hr = d3d11_video_device_->CreateVideoProcessor(video_processor_enum_, 0,
&video_processor_);
if (FAILED(hr))
return false;
return true;
}
void OUTPUTMANAGER::RenderD3D11Texture(int width, int height) {
HRESULT hr = S_OK;
if (m_SwapChain == nullptr) {
return;
}
Microsoft::WRL::ComPtr<ID3D11Texture2D> dxgi_back_buffer;
hr = m_SwapChain->GetBuffer(0, IID_PPV_ARGS(&dxgi_back_buffer));
if (FAILED(hr)) {
std::string message = std::system_category().message(hr);
return;
}
D3D11_TEXTURE2D_DESC back_buffer_desc;
dxgi_back_buffer->GetDesc(&back_buffer_desc);
D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC output_view_desc;
ZeroMemory(&output_view_desc, sizeof(output_view_desc));
output_view_desc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
output_view_desc.Texture2D.MipSlice = 0;
Microsoft::WRL::ComPtr<ID3D11VideoProcessorOutputView> output_view;
hr = d3d11_video_device_->CreateVideoProcessorOutputView(
dxgi_back_buffer.Get(), video_processor_enum_, &output_view_desc,
(ID3D11VideoProcessorOutputView**)&output_view);
if (FAILED(hr)) {
return;
}
D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC input_view_desc;
ZeroMemory(&input_view_desc, sizeof(input_view_desc));
input_view_desc.FourCC = 0;
input_view_desc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
input_view_desc.Texture2D.MipSlice = 0;
input_view_desc.Texture2D.ArraySlice = 0;
Microsoft::WRL::ComPtr<ID3D11VideoProcessorInputView> input_view;
hr = d3d11_video_device_->CreateVideoProcessorInputView(
m_SharedSurf, video_processor_enum_, &input_view_desc, (ID3D11VideoProcessorInputView**)&input_view);
if (FAILED(hr)) {
return;
}
D3D11_VIDEO_PROCESSOR_STREAM stream_data;
ZeroMemory(&stream_data, sizeof(stream_data));
stream_data.Enable = TRUE;
stream_data.OutputIndex = 0;
stream_data.InputFrameOrField = 0;
stream_data.PastFrames = 0;
stream_data.FutureFrames = 0;
stream_data.ppPastSurfaces = nullptr;
stream_data.ppFutureSurfaces = nullptr;
stream_data.pInputSurface = input_view.Get();
stream_data.ppPastSurfacesRight = nullptr;
stream_data.ppFutureSurfacesRight = nullptr;
stream_data.pInputSurfaceRight = nullptr;
RECT rect = { 0 };
rect.right = width;
rect.bottom = height;
d3d11_video_context_->VideoProcessorSetStreamSourceRect(video_processor_, 0,
true, &rect);
D3D11_VIDEO_COLOR color;
color.RGBA = { 0.f, 0.5f, 1.0f, 1.0f };
d3d11_video_context_->VideoProcessorSetOutputBackgroundColor(video_processor_, FALSE, &color);
RECT WindowRect;
GetClientRect(m_WindowHandle, &WindowRect);
WindowRect.left += 10;
WindowRect.top += 10;
WindowRect.right -= 50;
WindowRect.bottom -= 50;
d3d11_video_context_->VideoProcessorSetStreamDestRect(video_processor_, 0, true, &WindowRect);
d3d11_video_context_->VideoProcessorSetStreamFrameFormat(
video_processor_, 0, D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE);
hr = d3d11_video_context_->VideoProcessorBlt(
video_processor_, output_view.Get(), 0, 1, &stream_data);
if (FAILED(hr)) {
return;
}
hr = m_SwapChain->Present(1, 0);
if (FAILED(hr)) {
return;
}
}
_Post_satisfies_(return != DUPL_RETURN_SUCCESS)
DUPL_RETURN ProcessFailure(_In_opt_ ID3D11Device* Device, _In_ LPCWSTR Str, _In_ LPCWSTR Title, HRESULT hr, _In_opt_z_ HRESULT* ExpectedErrors)
{
HRESULT TranslatedHr;
// On an error check if the DX device is lost
if (Device)
{
HRESULT DeviceRemovedReason = Device->GetDeviceRemovedReason();
switch (DeviceRemovedReason)
{
case DXGI_ERROR_DEVICE_REMOVED:
case DXGI_ERROR_DEVICE_RESET:
case static_cast<HRESULT>(E_OUTOFMEMORY) :
{
// Our device has been stopped due to an external event on the GPU so map them all to
// device removed and continue processing the condition
TranslatedHr = DXGI_ERROR_DEVICE_REMOVED;
break;
}
case S_OK:
{
// Device is not removed so use original error
TranslatedHr = hr;
break;
}
default:
{
// Device is removed but not a error we want to remap
TranslatedHr = DeviceRemovedReason;
}
}
}
else
{
TranslatedHr = hr;
}
// Check if this error was expected or not
if (ExpectedErrors)
{
HRESULT* CurrentResult = ExpectedErrors;
while (*CurrentResult != S_OK)
{
if (*(CurrentResult++) == TranslatedHr)
{
return DUPL_RETURN_ERROR_EXPECTED;
}
}
}
// Error was not expected so display the message box
DisplayMsg(Str, Title, TranslatedHr);
return DUPL_RETURN_ERROR_UNEXPECTED;
}
//
// Displays a message
//
void DisplayMsg(_In_ LPCWSTR Str, _In_ LPCWSTR Title, HRESULT hr)
{
if (SUCCEEDED(hr))
{
MessageBoxW(nullptr, Str, Title, MB_OK);
return;
}
const UINT StringLen = (UINT)(wcslen(Str) + sizeof(" with HRESULT 0x########."));
wchar_t* OutStr = new wchar_t[StringLen];
if (!OutStr)
{
return;
}
INT LenWritten = swprintf_s(OutStr, StringLen, L"%s with 0x%X.", Str, hr);
if (LenWritten != -1)
{
MessageBoxW(nullptr, OutStr, Title, MB_OK);
}
delete[] OutStr;
}
D3D11RGBRendering.cpp
// D3D11RGBRendering.cpp : Defines the entry point for the application.
//
#include <stdio.h>
#include "D3D11RGBRendering.h"
#include "OutputManager.h"
#define MAX_LOADSTRING 100
char buf[1024];
UINT pitch = 0, bmpHeight = 768, bmpWidth = 1366;
enum RGB
{
RGB32,
RGB24
};
RGB rgb = RGB32; //Change it accordingly
//
// Globals
//
OUTPUTMANAGER OutMgr;
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
// Forward declarations of functions included in this code module:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void WriteBitmap32ToTexture(BYTE *bitmap, RECT DeskBounds);
void WriteBitmap24ToTexture(BYTE *bitmap, RECT DeskBounds);
BYTE* ReadBitmapFromFile();
//
// Program entry point
//
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ INT nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
INT SingleOutput = -1;
// Window
HWND WindowHandle = nullptr;
// Load simple cursor
HCURSOR Cursor = nullptr;
Cursor = LoadCursor(nullptr, IDC_ARROW);
if (!Cursor)
{
ProcessFailure(nullptr, L"Cursor load failed", L"Error", E_UNEXPECTED);
return 0;
}
// Register class
WNDCLASSEXW Wc;
Wc.cbSize = sizeof(WNDCLASSEXW);
Wc.style = CS_HREDRAW | CS_VREDRAW;
Wc.lpfnWndProc = WndProc;
Wc.cbClsExtra = 0;
Wc.cbWndExtra = 0;
Wc.hInstance = hInstance;
Wc.hIcon = nullptr;
Wc.hCursor = Cursor;
Wc.hbrBackground = nullptr;
Wc.lpszMenuName = nullptr;
Wc.lpszClassName = L"ddasample";
Wc.hIconSm = nullptr;
if (!RegisterClassExW(&Wc))
{
ProcessFailure(nullptr, L"Window class registration failed", L"Error", E_UNEXPECTED);
return 0;
}
// Create window
RECT WindowRect = { 0, 0, 1400, 800 };
AdjustWindowRect(&WindowRect, WS_OVERLAPPEDWINDOW, FALSE);
WindowHandle = CreateWindowW(L"ddasample", L"DXGI desktop duplication sample",
WS_OVERLAPPEDWINDOW,
0, 0,
WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top,
nullptr, nullptr, hInstance, nullptr);
if (!WindowHandle)
{
ProcessFailure(nullptr, L"Window creation failed", L"Error", E_FAIL);
return 0;
}
DestroyCursor(Cursor);
ShowWindow(WindowHandle, nCmdShow);
UpdateWindow(WindowHandle);
RECT DeskBounds;
UINT OutputCount;
// Message loop (attempts to update screen when no other messages to process)
MSG msg = { 0 };
bool FirstTime = true;
bool Occluded = true;
while (WM_QUIT != msg.message)
{
DUPL_RETURN Ret = DUPL_RETURN_SUCCESS;
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (msg.message == OCCLUSION_STATUS_MSG)
{
// Present may not be occluded now so try again
Occluded = false;
}
else
{
// Process window messages
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else if (FirstTime)
{
// First time through the loop so nothing to clean up
FirstTime = false;
BYTE* bitmap = ReadBitmapFromFile();
OutMgr.InitD3D11Video(WindowHandle, bmpWidth, bmpHeight);
// We start off in occluded state and we should immediate get a occlusion status window message
Occluded = false;
}
else
{
// Nothing else to do, so try to present to write out to window if not occluded
if (!Occluded)
{
BYTE *bitmap = ReadBitmapFromFile();
DeskBounds = { 0, 0, (LONG)bmpWidth, (LONG)bmpHeight };
(rgb == RGB32) ? WriteBitmap32ToTexture(bitmap, DeskBounds) : WriteBitmap24ToTexture(bitmap, DeskBounds);
free(bitmap);
//Ret = OutMgr.UpdateApplicationWindow(&Occluded);
OutMgr.PresentVideoFrame(bmpWidth, bmpHeight);
//d3d11->DrawFrame(bitmap);
//free(bitmap);
}
}
}
if (msg.message == WM_QUIT)
{
OutMgr.CleanRefs();
// For a WM_QUIT message we should return the wParam value
return static_cast<INT>(msg.wParam);
}
return 0;
}
BYTE* ReadBitmapFromFile()
{
FILE *file = nullptr;
sprintf_s(buf, (rgb == RGB32) ? "input\\rgb32.bmp" : "input\\rgb24.bmp");
fopen_s(&file, buf, "rb");
BITMAPFILEHEADER *bITMAPFILEHEADER = (BITMAPFILEHEADER*)malloc(sizeof(BITMAPFILEHEADER));
BITMAPINFOHEADER *bITMAPINFOHEADER = (BITMAPINFOHEADER*)malloc(sizeof(BITMAPINFOHEADER));
int readBytes = fread(bITMAPFILEHEADER, sizeof(BITMAPFILEHEADER), 1, file);
readBytes = fread(bITMAPINFOHEADER, sizeof(BITMAPINFOHEADER), 1, file);
size_t bitmapSize = bITMAPFILEHEADER->bfSize - sizeof(BITMAPFILEHEADER) - sizeof(BITMAPINFOHEADER);
BYTE *bitmap = (BYTE*)malloc(bitmapSize);
pitch = bitmapSize / bITMAPINFOHEADER->biHeight;
bmpHeight = bITMAPINFOHEADER->biHeight;
bmpWidth = bITMAPINFOHEADER->biWidth;
readBytes = fread(bitmap, bitmapSize, 1, file);
//memset(bitmap, 128, bitmapSize);
fclose(file);
return bitmap;
}
void WriteBitmap32ToTexture(BYTE *bitmap, RECT DeskBounds)
{
// Copy image into CPU access texture
OutMgr.m_DeviceContext->CopyResource(OutMgr.m_AccessibleSurf, OutMgr.m_SharedSurf);
// Copy from CPU access texture to bitmap buffer
D3D11_MAPPED_SUBRESOURCE resource;
UINT subresource = D3D11CalcSubresource(0, 0, 0);
OutMgr.m_DeviceContext->Map(OutMgr.m_AccessibleSurf, subresource, D3D11_MAP_WRITE, 0, &resource);
UINT dist = 0, height = DeskBounds.bottom - DeskBounds.top, sdist = 0;
BYTE* dptr = reinterpret_cast<BYTE*>(resource.pData);
dptr += resource.RowPitch*(height - 1);
int minPitch = min(pitch, resource.RowPitch);
//memcpy_s(dptr, resource.RowPitch*height, bitmap, resource.RowPitch*height);
for (size_t h = 0; h < height; ++h)
{
dist = resource.RowPitch *h;
sdist = pitch * h;
memcpy_s(dptr - dist, resource.RowPitch, bitmap + sdist, minPitch);
}
OutMgr.m_DeviceContext->Unmap(OutMgr.m_AccessibleSurf, subresource);
OutMgr.m_DeviceContext->CopyResource(OutMgr.m_SharedSurf, OutMgr.m_AccessibleSurf);
}
void WriteBitmap24ToTexture(BYTE *bitmap, RECT DeskBounds)
{
// Copy image into CPU access texture
OutMgr.m_DeviceContext->CopyResource(OutMgr.m_AccessibleSurf, OutMgr.m_SharedSurf);
// Copy from CPU access texture to bitmap buffer
D3D11_MAPPED_SUBRESOURCE resource;
UINT subresource = D3D11CalcSubresource(0, 0, 0);
OutMgr.m_DeviceContext->Map(OutMgr.m_AccessibleSurf, subresource, D3D11_MAP_WRITE, 0, &resource);
int dist = 0, height = DeskBounds.bottom - DeskBounds.top, sdist = 0;
BYTE* dptr = reinterpret_cast<BYTE*>(resource.pData);
dptr += resource.RowPitch*(height - 1);
for (size_t h = 0; h < bmpHeight; ++h)
{
dist = resource.RowPitch *h;
sdist = pitch * h;
for (size_t w = 0; w < bmpWidth; w++)
{
dptr[-dist + w * 4] = bitmap[sdist + w * 3];
dptr[-dist + w * 4 + 1] = bitmap[sdist + w * 3 + 1];
dptr[-dist + w * 4 + 2] = bitmap[sdist + w * 3 + 2];
}
}
OutMgr.m_DeviceContext->Unmap(OutMgr.m_AccessibleSurf, subresource);
OutMgr.m_DeviceContext->CopyResource(OutMgr.m_SharedSurf, OutMgr.m_AccessibleSurf);
}
//
// Window message processor
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
case WM_SIZE:
{
// Tell output manager that window size has changed
OutMgr.WindowResize();
break;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}