D3D11 render

发布时间 2023-04-17 19:01:04作者: GreyWang

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;
}