DXGI/OutputManager.cpp
2024-09-08 12:21:59 +08:00

1104 lines
36 KiB
C++

// 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"
using namespace DirectX;
//
// Constructor NULLs out all pointers & sets appropriate var vals
//
OUTPUTMANAGER::OUTPUTMANAGER() : m_SwapChain(nullptr),
m_Device(nullptr),
m_Factory(nullptr),
m_DeviceContext(nullptr),
m_RTV(nullptr),
m_SamplerLinear(nullptr),
m_BlendState(nullptr),
m_VertexShader(nullptr),
m_PixelShader(nullptr),
m_InputLayout(nullptr),
m_SharedSurf(nullptr),
m_KeyMutex(nullptr),
m_WindowHandle(nullptr),
m_NeedsResize(false),
m_OcclusionCookie(0)
{
}
//
// 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;
}
//
// Initialize all state
//
DUPL_RETURN OUTPUTMANAGER::InitOutput(HWND Window, INT SingleOutput, _Out_ UINT* OutCount, _Out_ RECT* DeskBounds)
{
HRESULT hr;
// Store window handle
m_WindowHandle = Window;
// Driver types supported
D3D_DRIVER_TYPE DriverTypes[] =
{
D3D_DRIVER_TYPE_HARDWARE,
D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_REFERENCE,
};
UINT NumDriverTypes = ARRAYSIZE(DriverTypes);
// Feature levels supported
D3D_FEATURE_LEVEL FeatureLevels[] =
{
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_1
};
UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels);
D3D_FEATURE_LEVEL FeatureLevel;
// Create device
for (UINT DriverTypeIndex = 0; DriverTypeIndex < NumDriverTypes; ++DriverTypeIndex)
{
hr = D3D11CreateDevice(nullptr, DriverTypes[DriverTypeIndex], nullptr, 0, FeatureLevels, NumFeatureLevels,
D3D11_SDK_VERSION, &m_Device, &FeatureLevel, &m_DeviceContext);
if (SUCCEEDED(hr))
{
// Device creation succeeded, no need to loop anymore
break;
}
}
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Device creation in OUTPUTMANAGER failed", L"Error", hr, SystemTransitionsExpectedErrors);
}
// Get DXGI factory
IDXGIDevice* DxgiDevice = nullptr;
hr = m_Device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&DxgiDevice));
if (FAILED(hr))
{
return ProcessFailure(nullptr, L"Failed to QI for DXGI Device", L"Error", hr, nullptr);
}
IDXGIAdapter* DxgiAdapter = nullptr;
hr = DxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&DxgiAdapter));
DxgiDevice->Release();
DxgiDevice = nullptr;
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to get parent DXGI Adapter", L"Error", hr, SystemTransitionsExpectedErrors);
}
hr = DxgiAdapter->GetParent(__uuidof(IDXGIFactory2), reinterpret_cast<void**>(&m_Factory));
DxgiAdapter->Release();
DxgiAdapter = nullptr;
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to get parent DXGI Factory", L"Error", hr, SystemTransitionsExpectedErrors);
}
// Register for occlusion status windows message
hr = m_Factory->RegisterOcclusionStatusWindow(Window, OCCLUSION_STATUS_MSG, &m_OcclusionCookie);
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to register for occlusion message", L"Error", hr, SystemTransitionsExpectedErrors);
}
// Get window size
RECT WindowRect;
GetClientRect(m_WindowHandle, &WindowRect);
UINT Width = WindowRect.right - WindowRect.left;
UINT Height = WindowRect.bottom - WindowRect.top;
// Create swapchain for window
DXGI_SWAP_CHAIN_DESC1 SwapChainDesc;
RtlZeroMemory(&SwapChainDesc, sizeof(SwapChainDesc));
SwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
SwapChainDesc.BufferCount = 2;
SwapChainDesc.Width = Width;
SwapChainDesc.Height = Height;
SwapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
SwapChainDesc.SampleDesc.Count = 1;
SwapChainDesc.SampleDesc.Quality = 0;
hr = m_Factory->CreateSwapChainForHwnd(m_Device, Window, &SwapChainDesc, nullptr, nullptr, &m_SwapChain);
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to create window swapchain", L"Error", hr, SystemTransitionsExpectedErrors);
}
// Disable the ALT-ENTER shortcut for entering full-screen mode
hr = m_Factory->MakeWindowAssociation(Window, DXGI_MWA_NO_ALT_ENTER);
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to make window association", L"Error", hr, SystemTransitionsExpectedErrors);
}
// Create shared texture
DUPL_RETURN Return = CreateSharedSurf(SingleOutput, OutCount, DeskBounds);
if (Return != DUPL_RETURN_SUCCESS)
{
return Return;
}
// Make new render target view
Return = MakeRTV();
if (Return != DUPL_RETURN_SUCCESS)
{
return Return;
}
// Set view port
SetViewPort(Width, Height);
// Create the sample state
D3D11_SAMPLER_DESC SampDesc;
RtlZeroMemory(&SampDesc, sizeof(SampDesc));
SampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
SampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
SampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
SampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
SampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
SampDesc.MinLOD = 0;
SampDesc.MaxLOD = D3D11_FLOAT32_MAX;
hr = m_Device->CreateSamplerState(&SampDesc, &m_SamplerLinear);
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to create sampler state in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors);
}
// Create the blend state
D3D11_BLEND_DESC BlendStateDesc;
BlendStateDesc.AlphaToCoverageEnable = FALSE;
BlendStateDesc.IndependentBlendEnable = FALSE;
BlendStateDesc.RenderTarget[0].BlendEnable = TRUE;
BlendStateDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
BlendStateDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
BlendStateDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
BlendStateDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
BlendStateDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
BlendStateDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
BlendStateDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
hr = m_Device->CreateBlendState(&BlendStateDesc, &m_BlendState);
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to create blend state in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors);
}
// Initialize shaders
Return = InitShaders();
if (Return != DUPL_RETURN_SUCCESS)
{
return Return;
}
GetWindowRect(m_WindowHandle, &WindowRect);
MoveWindow(m_WindowHandle, WindowRect.left, WindowRect.top, (DeskBounds->right - DeskBounds->left) / 2, (DeskBounds->bottom - DeskBounds->top) / 2, TRUE);
return Return;
}
//
// Recreate shared texture
//
DUPL_RETURN OUTPUTMANAGER::CreateSharedSurf(INT SingleOutput, _Out_ UINT* OutCount, _Out_ RECT* DeskBounds)
{
HRESULT hr;
// Get DXGI resources
IDXGIDevice* DxgiDevice = nullptr;
hr = m_Device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&DxgiDevice));
if (FAILED(hr))
{
return ProcessFailure(nullptr, L"Failed to QI for DXGI Device", L"Error", hr);
}
IDXGIAdapter* DxgiAdapter = nullptr;
hr = DxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&DxgiAdapter));
DxgiDevice->Release();
DxgiDevice = nullptr;
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to get parent DXGI Adapter", L"Error", hr, SystemTransitionsExpectedErrors);
}
// Set initial values so that we always catch the right coordinates
DeskBounds->left = INT_MAX;
DeskBounds->right = INT_MIN;
DeskBounds->top = INT_MAX;
DeskBounds->bottom = INT_MIN;
IDXGIOutput* DxgiOutput = nullptr;
// Figure out right dimensions for full size desktop texture and # of outputs to duplicate
UINT OutputCount;
if (SingleOutput < 0)
{
hr = S_OK;
for (OutputCount = 0; SUCCEEDED(hr); ++OutputCount)
{
if (DxgiOutput)
{
DxgiOutput->Release();
DxgiOutput = nullptr;
}
hr = DxgiAdapter->EnumOutputs(OutputCount, &DxgiOutput);
if (DxgiOutput && (hr != DXGI_ERROR_NOT_FOUND))
{
DXGI_OUTPUT_DESC DesktopDesc;
DxgiOutput->GetDesc(&DesktopDesc);
DeskBounds->left = min(DesktopDesc.DesktopCoordinates.left, DeskBounds->left);
DeskBounds->top = min(DesktopDesc.DesktopCoordinates.top, DeskBounds->top);
DeskBounds->right = max(DesktopDesc.DesktopCoordinates.right, DeskBounds->right);
DeskBounds->bottom = max(DesktopDesc.DesktopCoordinates.bottom, DeskBounds->bottom);
}
}
--OutputCount;
}
else
{
hr = DxgiAdapter->EnumOutputs(SingleOutput, &DxgiOutput);
if (FAILED(hr))
{
DxgiAdapter->Release();
DxgiAdapter = nullptr;
return ProcessFailure(m_Device, L"Output specified to be duplicated does not exist", L"Error", hr);
}
DXGI_OUTPUT_DESC DesktopDesc;
DxgiOutput->GetDesc(&DesktopDesc);
*DeskBounds = DesktopDesc.DesktopCoordinates;
DxgiOutput->Release();
DxgiOutput = nullptr;
OutputCount = 1;
}
DxgiAdapter->Release();
DxgiAdapter = nullptr;
// Set passed in output count variable
*OutCount = OutputCount;
if (OutputCount == 0)
{
// We could not find any outputs, the system must be in a transition so return expected error
// so we will attempt to recreate
return DUPL_RETURN_ERROR_EXPECTED;
}
// Create shared texture for all duplication 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;
DeskTexD.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
hr = m_Device->CreateTexture2D(&DeskTexD, nullptr, &m_SharedSurf);
if (FAILED(hr))
{
if (OutputCount != 1)
{
// If we are duplicating the complete desktop we try to create a single texture to hold the
// complete desktop image and blit updates from the per output DDA interface. The GPU can
// always support a texture size of the maximum resolution of any single output but there is no
// guarantee that it can support a texture size of the desktop.
// The sample only use this large texture to display the desktop image in a single window using DX
// we could revert back to using GDI to update the window in this failure case.
return ProcessFailure(m_Device, L"Failed to create DirectX shared texture - we are attempting to create a texture the size of the complete desktop and this may be larger than the maximum texture size of your GPU. Please try again using the -output command line parameter to duplicate only 1 monitor or configure your computer to a single monitor configuration", L"Error", hr, SystemTransitionsExpectedErrors);
}
else
{
return ProcessFailure(m_Device, L"Failed to create shared texture", L"Error", hr, SystemTransitionsExpectedErrors);
}
}
// Get keyed mutex
hr = m_SharedSurf->QueryInterface(__uuidof(IDXGIKeyedMutex), reinterpret_cast<void**>(&m_KeyMutex));
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to query for keyed mutex in OUTPUTMANAGER", L"Error", hr);
}
return DUPL_RETURN_SUCCESS;
}
//
// Present to the application window
//
DUPL_RETURN OUTPUTMANAGER::UpdateApplicationWindow(_In_ PTR_INFO* PointerInfo, _Inout_ bool* Occluded)
{
// In a typical desktop duplication application there would be an application running on one system collecting the desktop images
// and another application running on a different system that receives the desktop images via a network and display the image. This
// sample contains both these aspects into a single application.
// This routine is the part of the sample that displays the desktop image onto the display
// Try and acquire sync on common display buffer
HRESULT hr = m_KeyMutex->AcquireSync(1, 100);
if (hr == static_cast<HRESULT>(WAIT_TIMEOUT))
{
// Another thread has the keyed mutex so try again later
return DUPL_RETURN_SUCCESS;
}
else if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to acquire Keyed mutex in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors);
}
// Got mutex, so draw
DUPL_RETURN Ret = DrawFrame();
if (Ret == DUPL_RETURN_SUCCESS)
{
// We have keyed mutex so we can access the mouse info
if (PointerInfo->Visible)
{
// Draw mouse into texture
Ret = DrawMouse(PointerInfo);
}
}
// Release keyed mutex
hr = m_KeyMutex->ReleaseSync(0);
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to Release Keyed mutex in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors);
}
// Present to window if all worked
if (Ret == DUPL_RETURN_SUCCESS)
{
// Present to window
hr = m_SwapChain->Present(1, 0);
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to present", L"Error", hr, SystemTransitionsExpectedErrors);
}
else if (hr == DXGI_STATUS_OCCLUDED)
{
*Occluded = true;
}
}
return Ret;
}
//
// 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;
}
//
// Draw frame into backbuffer
//
DUPL_RETURN OUTPUTMANAGER::DrawFrame()
{
HRESULT hr;
// If window was resized, resize swapchain
if (m_NeedsResize)
{
DUPL_RETURN Ret = ResizeSwapChain();
if (Ret != DUPL_RETURN_SUCCESS)
{
return Ret;
}
m_NeedsResize = false;
}
// Vertices for drawing whole texture
VERTEX Vertices[NUMVERTICES] =
{
{XMFLOAT3(-1.0f, -1.0f, 0), XMFLOAT2(0.0f, 1.0f)},
{XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)},
{XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)},
{XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)},
{XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)},
{XMFLOAT3(1.0f, 1.0f, 0), XMFLOAT2(1.0f, 0.0f)},
};
D3D11_TEXTURE2D_DESC FrameDesc;
m_SharedSurf->GetDesc(&FrameDesc);
D3D11_SHADER_RESOURCE_VIEW_DESC ShaderDesc;
ShaderDesc.Format = FrameDesc.Format;
ShaderDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
ShaderDesc.Texture2D.MostDetailedMip = FrameDesc.MipLevels - 1;
ShaderDesc.Texture2D.MipLevels = FrameDesc.MipLevels;
// Create new shader resource view
ID3D11ShaderResourceView* ShaderResource = nullptr;
hr = m_Device->CreateShaderResourceView(m_SharedSurf, &ShaderDesc, &ShaderResource);
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to create shader resource when drawing a frame", L"Error", hr, SystemTransitionsExpectedErrors);
}
// Set resources
UINT Stride = sizeof(VERTEX);
UINT Offset = 0;
FLOAT blendFactor[4] = {0.f, 0.f, 0.f, 0.f};
m_DeviceContext->OMSetBlendState(nullptr, blendFactor, 0xffffffff);
m_DeviceContext->OMSetRenderTargets(1, &m_RTV, nullptr);
m_DeviceContext->VSSetShader(m_VertexShader, nullptr, 0);
m_DeviceContext->PSSetShader(m_PixelShader, nullptr, 0);
m_DeviceContext->PSSetShaderResources(0, 1, &ShaderResource);
m_DeviceContext->PSSetSamplers(0, 1, &m_SamplerLinear);
m_DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
D3D11_BUFFER_DESC BufferDesc;
RtlZeroMemory(&BufferDesc, sizeof(BufferDesc));
BufferDesc.Usage = D3D11_USAGE_DEFAULT;
BufferDesc.ByteWidth = sizeof(VERTEX) * NUMVERTICES;
BufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
BufferDesc.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA InitData;
RtlZeroMemory(&InitData, sizeof(InitData));
InitData.pSysMem = Vertices;
ID3D11Buffer* VertexBuffer = nullptr;
// Create vertex buffer
hr = m_Device->CreateBuffer(&BufferDesc, &InitData, &VertexBuffer);
if (FAILED(hr))
{
ShaderResource->Release();
ShaderResource = nullptr;
return ProcessFailure(m_Device, L"Failed to create vertex buffer when drawing a frame", L"Error", hr, SystemTransitionsExpectedErrors);
}
m_DeviceContext->IASetVertexBuffers(0, 1, &VertexBuffer, &Stride, &Offset);
// Draw textured quad onto render target
m_DeviceContext->Draw(NUMVERTICES, 0);
VertexBuffer->Release();
VertexBuffer = nullptr;
// Release shader resource
ShaderResource->Release();
ShaderResource = nullptr;
return DUPL_RETURN_SUCCESS;
}
//
// Process both masked and monochrome pointers
//
DUPL_RETURN OUTPUTMANAGER::ProcessMonoMask(bool IsMono, _Inout_ PTR_INFO* PtrInfo, _Out_ INT* PtrWidth, _Out_ INT* PtrHeight, _Out_ INT* PtrLeft, _Out_ INT* PtrTop, _Outptr_result_bytebuffer_(*PtrHeight * *PtrWidth * BPP) BYTE** InitBuffer, _Out_ D3D11_BOX* Box)
{
// Desktop dimensions
D3D11_TEXTURE2D_DESC FullDesc;
m_SharedSurf->GetDesc(&FullDesc);
INT DesktopWidth = FullDesc.Width;
INT DesktopHeight = FullDesc.Height;
// Pointer position
INT GivenLeft = PtrInfo->Position.x;
INT GivenTop = PtrInfo->Position.y;
// Figure out if any adjustment is needed for out of bound positions
if (GivenLeft < 0)
{
*PtrWidth = GivenLeft + static_cast<INT>(PtrInfo->ShapeInfo.Width);
}
else if ((GivenLeft + static_cast<INT>(PtrInfo->ShapeInfo.Width)) > DesktopWidth)
{
*PtrWidth = DesktopWidth - GivenLeft;
}
else
{
*PtrWidth = static_cast<INT>(PtrInfo->ShapeInfo.Width);
}
if (IsMono)
{
PtrInfo->ShapeInfo.Height = PtrInfo->ShapeInfo.Height / 2;
}
if (GivenTop < 0)
{
*PtrHeight = GivenTop + static_cast<INT>(PtrInfo->ShapeInfo.Height);
}
else if ((GivenTop + static_cast<INT>(PtrInfo->ShapeInfo.Height)) > DesktopHeight)
{
*PtrHeight = DesktopHeight - GivenTop;
}
else
{
*PtrHeight = static_cast<INT>(PtrInfo->ShapeInfo.Height);
}
if (IsMono)
{
PtrInfo->ShapeInfo.Height = PtrInfo->ShapeInfo.Height * 2;
}
*PtrLeft = (GivenLeft < 0) ? 0 : GivenLeft;
*PtrTop = (GivenTop < 0) ? 0 : GivenTop;
// Staging buffer/texture
D3D11_TEXTURE2D_DESC CopyBufferDesc;
CopyBufferDesc.Width = *PtrWidth;
CopyBufferDesc.Height = *PtrHeight;
CopyBufferDesc.MipLevels = 1;
CopyBufferDesc.ArraySize = 1;
CopyBufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
CopyBufferDesc.SampleDesc.Count = 1;
CopyBufferDesc.SampleDesc.Quality = 0;
CopyBufferDesc.Usage = D3D11_USAGE_STAGING;
CopyBufferDesc.BindFlags = 0;
CopyBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
CopyBufferDesc.MiscFlags = 0;
ID3D11Texture2D* CopyBuffer = nullptr;
HRESULT hr = m_Device->CreateTexture2D(&CopyBufferDesc, nullptr, &CopyBuffer);
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed creating staging texture for pointer", L"Error", hr, SystemTransitionsExpectedErrors);
}
// Copy needed part of desktop image
Box->left = *PtrLeft;
Box->top = *PtrTop;
Box->right = *PtrLeft + *PtrWidth;
Box->bottom = *PtrTop + *PtrHeight;
m_DeviceContext->CopySubresourceRegion(CopyBuffer, 0, 0, 0, 0, m_SharedSurf, 0, Box);
// QI for IDXGISurface
IDXGISurface* CopySurface = nullptr;
hr = CopyBuffer->QueryInterface(__uuidof(IDXGISurface), (void **)&CopySurface);
CopyBuffer->Release();
CopyBuffer = nullptr;
if (FAILED(hr))
{
return ProcessFailure(nullptr, L"Failed to QI staging texture into IDXGISurface for pointer", L"Error", hr, SystemTransitionsExpectedErrors);
}
// Map pixels
DXGI_MAPPED_RECT MappedSurface;
hr = CopySurface->Map(&MappedSurface, DXGI_MAP_READ);
if (FAILED(hr))
{
CopySurface->Release();
CopySurface = nullptr;
return ProcessFailure(m_Device, L"Failed to map surface for pointer", L"Error", hr, SystemTransitionsExpectedErrors);
}
// New mouseshape buffer
*InitBuffer = new (std::nothrow) BYTE[*PtrWidth * *PtrHeight * BPP];
if (!(*InitBuffer))
{
return ProcessFailure(nullptr, L"Failed to allocate memory for new mouse shape buffer.", L"Error", E_OUTOFMEMORY);
}
UINT* InitBuffer32 = reinterpret_cast<UINT*>(*InitBuffer);
UINT* Desktop32 = reinterpret_cast<UINT*>(MappedSurface.pBits);
UINT DesktopPitchInPixels = MappedSurface.Pitch / sizeof(UINT);
// What to skip (pixel offset)
UINT SkipX = (GivenLeft < 0) ? (-1 * GivenLeft) : (0);
UINT SkipY = (GivenTop < 0) ? (-1 * GivenTop) : (0);
if (IsMono)
{
for (INT Row = 0; Row < *PtrHeight; ++Row)
{
// Set mask
BYTE Mask = 0x80;
Mask = Mask >> (SkipX % 8);
for (INT Col = 0; Col < *PtrWidth; ++Col)
{
// Get masks using appropriate offsets
BYTE AndMask = PtrInfo->PtrShapeBuffer[((Col + SkipX) / 8) + ((Row + SkipY) * (PtrInfo->ShapeInfo.Pitch))] & Mask;
BYTE XorMask = PtrInfo->PtrShapeBuffer[((Col + SkipX) / 8) + ((Row + SkipY + (PtrInfo->ShapeInfo.Height / 2)) * (PtrInfo->ShapeInfo.Pitch))] & Mask;
UINT AndMask32 = (AndMask) ? 0xFFFFFFFF : 0xFF000000;
UINT XorMask32 = (XorMask) ? 0x00FFFFFF : 0x00000000;
// Set new pixel
InitBuffer32[(Row * *PtrWidth) + Col] = (Desktop32[(Row * DesktopPitchInPixels) + Col] & AndMask32) ^ XorMask32;
// Adjust mask
if (Mask == 0x01)
{
Mask = 0x80;
}
else
{
Mask = Mask >> 1;
}
}
}
}
else
{
UINT* Buffer32 = reinterpret_cast<UINT*>(PtrInfo->PtrShapeBuffer);
// Iterate through pixels
for (INT Row = 0; Row < *PtrHeight; ++Row)
{
for (INT Col = 0; Col < *PtrWidth; ++Col)
{
// Set up mask
UINT MaskVal = 0xFF000000 & Buffer32[(Col + SkipX) + ((Row + SkipY) * (PtrInfo->ShapeInfo.Pitch / sizeof(UINT)))];
if (MaskVal)
{
// Mask was 0xFF
InitBuffer32[(Row * *PtrWidth) + Col] = (Desktop32[(Row * DesktopPitchInPixels) + Col] ^ Buffer32[(Col + SkipX) + ((Row + SkipY) * (PtrInfo->ShapeInfo.Pitch / sizeof(UINT)))]) | 0xFF000000;
}
else
{
// Mask was 0x00
InitBuffer32[(Row * *PtrWidth) + Col] = Buffer32[(Col + SkipX) + ((Row + SkipY) * (PtrInfo->ShapeInfo.Pitch / sizeof(UINT)))] | 0xFF000000;
}
}
}
}
// Done with resource
hr = CopySurface->Unmap();
CopySurface->Release();
CopySurface = nullptr;
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to unmap surface for pointer", L"Error", hr, SystemTransitionsExpectedErrors);
}
return DUPL_RETURN_SUCCESS;
}
//
// Draw mouse provided in buffer to backbuffer
//
DUPL_RETURN OUTPUTMANAGER::DrawMouse(_In_ PTR_INFO* PtrInfo)
{
// Vars to be used
ID3D11Texture2D* MouseTex = nullptr;
ID3D11ShaderResourceView* ShaderRes = nullptr;
ID3D11Buffer* VertexBufferMouse = nullptr;
D3D11_SUBRESOURCE_DATA InitData;
D3D11_TEXTURE2D_DESC Desc;
D3D11_SHADER_RESOURCE_VIEW_DESC SDesc;
// Position will be changed based on mouse position
VERTEX Vertices[NUMVERTICES] =
{
{XMFLOAT3(-1.0f, -1.0f, 0), XMFLOAT2(0.0f, 1.0f)},
{XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)},
{XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)},
{XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)},
{XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)},
{XMFLOAT3(1.0f, 1.0f, 0), XMFLOAT2(1.0f, 0.0f)},
};
D3D11_TEXTURE2D_DESC FullDesc;
m_SharedSurf->GetDesc(&FullDesc);
INT DesktopWidth = FullDesc.Width;
INT DesktopHeight = FullDesc.Height;
// Center of desktop dimensions
INT CenterX = (DesktopWidth / 2);
INT CenterY = (DesktopHeight / 2);
// Clipping adjusted coordinates / dimensions
INT PtrWidth = 0;
INT PtrHeight = 0;
INT PtrLeft = 0;
INT PtrTop = 0;
// Buffer used if necessary (in case of monochrome or masked pointer)
BYTE* InitBuffer = nullptr;
// Used for copying pixels
D3D11_BOX Box;
Box.front = 0;
Box.back = 1;
Desc.MipLevels = 1;
Desc.ArraySize = 1;
Desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
Desc.SampleDesc.Count = 1;
Desc.SampleDesc.Quality = 0;
Desc.Usage = D3D11_USAGE_DEFAULT;
Desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
Desc.CPUAccessFlags = 0;
Desc.MiscFlags = 0;
// Set shader resource properties
SDesc.Format = Desc.Format;
SDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
SDesc.Texture2D.MostDetailedMip = Desc.MipLevels - 1;
SDesc.Texture2D.MipLevels = Desc.MipLevels;
switch (PtrInfo->ShapeInfo.Type)
{
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
{
PtrLeft = PtrInfo->Position.x;
PtrTop = PtrInfo->Position.y;
PtrWidth = static_cast<INT>(PtrInfo->ShapeInfo.Width);
PtrHeight = static_cast<INT>(PtrInfo->ShapeInfo.Height);
break;
}
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME:
{
ProcessMonoMask(true, PtrInfo, &PtrWidth, &PtrHeight, &PtrLeft, &PtrTop, &InitBuffer, &Box);
break;
}
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
{
ProcessMonoMask(false, PtrInfo, &PtrWidth, &PtrHeight, &PtrLeft, &PtrTop, &InitBuffer, &Box);
break;
}
default:
break;
}
// VERTEX creation
Vertices[0].Pos.x = (PtrLeft - CenterX) / (FLOAT)CenterX;
Vertices[0].Pos.y = -1 * ((PtrTop + PtrHeight) - CenterY) / (FLOAT)CenterY;
Vertices[1].Pos.x = (PtrLeft - CenterX) / (FLOAT)CenterX;
Vertices[1].Pos.y = -1 * (PtrTop - CenterY) / (FLOAT)CenterY;
Vertices[2].Pos.x = ((PtrLeft + PtrWidth) - CenterX) / (FLOAT)CenterX;
Vertices[2].Pos.y = -1 * ((PtrTop + PtrHeight) - CenterY) / (FLOAT)CenterY;
Vertices[3].Pos.x = Vertices[2].Pos.x;
Vertices[3].Pos.y = Vertices[2].Pos.y;
Vertices[4].Pos.x = Vertices[1].Pos.x;
Vertices[4].Pos.y = Vertices[1].Pos.y;
Vertices[5].Pos.x = ((PtrLeft + PtrWidth) - CenterX) / (FLOAT)CenterX;
Vertices[5].Pos.y = -1 * (PtrTop - CenterY) / (FLOAT)CenterY;
// Set texture properties
Desc.Width = PtrWidth;
Desc.Height = PtrHeight;
// Set up init data
InitData.pSysMem = (PtrInfo->ShapeInfo.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR) ? PtrInfo->PtrShapeBuffer : InitBuffer;
InitData.SysMemPitch = (PtrInfo->ShapeInfo.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR) ? PtrInfo->ShapeInfo.Pitch : PtrWidth * BPP;
InitData.SysMemSlicePitch = 0;
// Create mouseshape as texture
HRESULT hr = m_Device->CreateTexture2D(&Desc, &InitData, &MouseTex);
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to create mouse pointer texture", L"Error", hr, SystemTransitionsExpectedErrors);
}
// Create shader resource from texture
hr = m_Device->CreateShaderResourceView(MouseTex, &SDesc, &ShaderRes);
if (FAILED(hr))
{
MouseTex->Release();
MouseTex = nullptr;
return ProcessFailure(m_Device, L"Failed to create shader resource from mouse pointer texture", L"Error", hr, SystemTransitionsExpectedErrors);
}
D3D11_BUFFER_DESC BDesc;
ZeroMemory(&BDesc, sizeof(D3D11_BUFFER_DESC));
BDesc.Usage = D3D11_USAGE_DEFAULT;
BDesc.ByteWidth = sizeof(VERTEX) * NUMVERTICES;
BDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
BDesc.CPUAccessFlags = 0;
ZeroMemory(&InitData, sizeof(D3D11_SUBRESOURCE_DATA));
InitData.pSysMem = Vertices;
// Create vertex buffer
hr = m_Device->CreateBuffer(&BDesc, &InitData, &VertexBufferMouse);
if (FAILED(hr))
{
ShaderRes->Release();
ShaderRes = nullptr;
MouseTex->Release();
MouseTex = nullptr;
return ProcessFailure(m_Device, L"Failed to create mouse pointer vertex buffer in OutputManager", L"Error", hr, SystemTransitionsExpectedErrors);
}
// Set resources
FLOAT BlendFactor[4] = {0.f, 0.f, 0.f, 0.f};
UINT Stride = sizeof(VERTEX);
UINT Offset = 0;
m_DeviceContext->IASetVertexBuffers(0, 1, &VertexBufferMouse, &Stride, &Offset);
m_DeviceContext->OMSetBlendState(m_BlendState, BlendFactor, 0xFFFFFFFF);
m_DeviceContext->OMSetRenderTargets(1, &m_RTV, nullptr);
m_DeviceContext->VSSetShader(m_VertexShader, nullptr, 0);
m_DeviceContext->PSSetShader(m_PixelShader, nullptr, 0);
m_DeviceContext->PSSetShaderResources(0, 1, &ShaderRes);
m_DeviceContext->PSSetSamplers(0, 1, &m_SamplerLinear);
// Draw
m_DeviceContext->Draw(NUMVERTICES, 0);
// Clean
if (VertexBufferMouse)
{
VertexBufferMouse->Release();
VertexBufferMouse = nullptr;
}
if (ShaderRes)
{
ShaderRes->Release();
ShaderRes = nullptr;
}
if (MouseTex)
{
MouseTex->Release();
MouseTex = nullptr;
}
if (InitBuffer)
{
delete [] InitBuffer;
InitBuffer = nullptr;
}
return DUPL_RETURN_SUCCESS;
}
//
// Initialize shaders for drawing to screen
//
DUPL_RETURN OUTPUTMANAGER::InitShaders()
{
HRESULT hr;
UINT Size = ARRAYSIZE(g_VS);
hr = m_Device->CreateVertexShader(g_VS, Size, nullptr, &m_VertexShader);
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to create vertex shader in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors);
}
D3D11_INPUT_ELEMENT_DESC Layout[] =
{
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}
};
UINT NumElements = ARRAYSIZE(Layout);
hr = m_Device->CreateInputLayout(Layout, NumElements, g_VS, Size, &m_InputLayout);
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to create input layout in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors);
}
m_DeviceContext->IASetInputLayout(m_InputLayout);
Size = ARRAYSIZE(g_PS);
hr = m_Device->CreatePixelShader(g_PS, Size, nullptr, &m_PixelShader);
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to create pixel shader in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors);
}
return DUPL_RETURN_SUCCESS;
}
//
// Reset render target view
//
DUPL_RETURN OUTPUTMANAGER::MakeRTV()
{
// Get backbuffer
ID3D11Texture2D* BackBuffer = nullptr;
HRESULT hr = m_SwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&BackBuffer));
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to get backbuffer for making render target view in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors);
}
// Create a render target view
hr = m_Device->CreateRenderTargetView(BackBuffer, nullptr, &m_RTV);
BackBuffer->Release();
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to create render target view in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors);
}
// Set new render target
m_DeviceContext->OMSetRenderTargets(1, &m_RTV, nullptr);
return DUPL_RETURN_SUCCESS;
}
//
// Set new viewport
//
void OUTPUTMANAGER::SetViewPort(UINT Width, UINT Height)
{
D3D11_VIEWPORT VP;
VP.Width = static_cast<FLOAT>(Width);
VP.Height = static_cast<FLOAT>(Height);
VP.MinDepth = 0.0f;
VP.MaxDepth = 1.0f;
VP.TopLeftX = 0;
VP.TopLeftY = 0;
m_DeviceContext->RSSetViewports(1, &VP);
}
//
// Resize swapchain
//
DUPL_RETURN OUTPUTMANAGER::ResizeSwapChain()
{
if (m_RTV)
{
m_RTV->Release();
m_RTV = nullptr;
}
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);
}
// Make new render target view
DUPL_RETURN Ret = MakeRTV();
if (Ret != DUPL_RETURN_SUCCESS)
{
return Ret;
}
// Set new viewport
SetViewPort(Width, Height);
return Ret;
}
//
// Releases all references
//
void OUTPUTMANAGER::CleanRefs()
{
if (m_VertexShader)
{
m_VertexShader->Release();
m_VertexShader = nullptr;
}
if (m_PixelShader)
{
m_PixelShader->Release();
m_PixelShader = nullptr;
}
if (m_InputLayout)
{
m_InputLayout->Release();
m_InputLayout = nullptr;
}
if (m_RTV)
{
m_RTV->Release();
m_RTV = nullptr;
}
if (m_SamplerLinear)
{
m_SamplerLinear->Release();
m_SamplerLinear = nullptr;
}
if (m_BlendState)
{
m_BlendState->Release();
m_BlendState = nullptr;
}
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_KeyMutex)
{
m_KeyMutex->Release();
m_KeyMutex = nullptr;
}
if (m_Factory)
{
if (m_OcclusionCookie)
{
m_Factory->UnregisterOcclusionStatus(m_OcclusionCookie);
m_OcclusionCookie = 0;
}
m_Factory->Release();
m_Factory = nullptr;
}
}