// 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(&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(&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(&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 if (m_WindowHandle) { 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 ? m_WindowHandle : GetDesktopWindow(), &WindowRect); UINT Width = WindowRect.right - WindowRect.left; UINT Height = WindowRect.bottom - WindowRect.top; if (m_WindowHandle) { // 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 if (m_WindowHandle) { 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); } if (m_WindowHandle) { // 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(&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(&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(&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(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 if (m_SwapChain) { 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(&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(PtrInfo->ShapeInfo.Width); } else if ((GivenLeft + static_cast(PtrInfo->ShapeInfo.Width)) > DesktopWidth) { *PtrWidth = DesktopWidth - GivenLeft; } else { *PtrWidth = static_cast(PtrInfo->ShapeInfo.Width); } if (IsMono) { PtrInfo->ShapeInfo.Height = PtrInfo->ShapeInfo.Height / 2; } if (GivenTop < 0) { *PtrHeight = GivenTop + static_cast(PtrInfo->ShapeInfo.Height); } else if ((GivenTop + static_cast(PtrInfo->ShapeInfo.Height)) > DesktopHeight) { *PtrHeight = DesktopHeight - GivenTop; } else { *PtrHeight = static_cast(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(*InitBuffer); UINT* Desktop32 = reinterpret_cast(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(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(PtrInfo->ShapeInfo.Width); PtrHeight = static_cast(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(&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(Width); VP.Height = static_cast(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; } }