mirror of
				https://github.com/gnif/LookingGlass.git
				synced 2025-11-04 06:31:54 +00:00 
			
		
		
		
	[host] add new D12 capture interface
Note, this capture interface is not yet feature complete but does seem to be stable.
This commit is contained in:
		@@ -106,7 +106,7 @@ typedef struct CapturePointer
 | 
			
		||||
CapturePointer;
 | 
			
		||||
 | 
			
		||||
typedef bool (*CaptureGetPointerBuffer )(void ** data, uint32_t * size);
 | 
			
		||||
typedef void (*CapturePostPointerBuffer)(CapturePointer pointer);
 | 
			
		||||
typedef void (*CapturePostPointerBuffer)(const CapturePointer * pointer);
 | 
			
		||||
 | 
			
		||||
typedef struct CaptureInterface
 | 
			
		||||
{
 | 
			
		||||
@@ -116,19 +116,27 @@ typedef struct CaptureInterface
 | 
			
		||||
  void         (*initOptions    )(void);
 | 
			
		||||
 | 
			
		||||
  bool(*create)(
 | 
			
		||||
    void                   * ivshmemBase,
 | 
			
		||||
    CaptureGetPointerBuffer  getPointerBufferFn,
 | 
			
		||||
    CapturePostPointerBuffer postPointerBufferFn
 | 
			
		||||
    CapturePostPointerBuffer postPointerBufferFn,
 | 
			
		||||
    unsigned                 frameBuffers
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  bool          (*init         )(unsigned * alignSize);
 | 
			
		||||
  bool          (*init         )(void * ivshmemBase, unsigned * alignSize);
 | 
			
		||||
  bool          (*start        )(void);
 | 
			
		||||
  void          (*stop         )(void);
 | 
			
		||||
  bool          (*deinit       )(void);
 | 
			
		||||
  void          (*free         )(void);
 | 
			
		||||
 | 
			
		||||
  CaptureResult (*capture   )(unsigned frameBufferIndex, FrameBuffer * frame);
 | 
			
		||||
  CaptureResult (*waitFrame )(CaptureFrame * frame, const size_t maxFrameSize);
 | 
			
		||||
  CaptureResult (*getFrame  )(FrameBuffer  * frame, int frameIndex);
 | 
			
		||||
  CaptureResult (*capture   )(
 | 
			
		||||
    unsigned frameBufferIndex,
 | 
			
		||||
    FrameBuffer * frame);
 | 
			
		||||
  CaptureResult (*waitFrame )(
 | 
			
		||||
    unsigned frameBufferIndex,
 | 
			
		||||
    CaptureFrame * frame,
 | 
			
		||||
    const size_t maxFrameSize);
 | 
			
		||||
  CaptureResult (*getFrame  )(
 | 
			
		||||
    unsigned frameBufferIndex,
 | 
			
		||||
    FrameBuffer  * frame,
 | 
			
		||||
    const size_t maxFrameSize);
 | 
			
		||||
}
 | 
			
		||||
CaptureInterface;
 | 
			
		||||
 
 | 
			
		||||
@@ -10,11 +10,14 @@ add_library(platform_Windows STATIC
 | 
			
		||||
  src/service.c
 | 
			
		||||
  src/mousehook.c
 | 
			
		||||
  src/force_compose.c
 | 
			
		||||
  src/com_ref.c
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# allow use of functions for Windows 7 or later
 | 
			
		||||
add_compile_definitions(WINVER=0x0601 _WIN32_WINNT=0x0601)
 | 
			
		||||
 | 
			
		||||
add_definitions("-DCOBJMACROS -DINITGUID -DWIDL_C_INLINE_WRAPPERS")
 | 
			
		||||
 | 
			
		||||
add_subdirectory("capture")
 | 
			
		||||
 | 
			
		||||
target_link_libraries(platform_Windows
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,9 @@ project(capture LANGUAGES C)
 | 
			
		||||
 | 
			
		||||
include(PreCapture)
 | 
			
		||||
 | 
			
		||||
option(USE_NVFBC "Enable NVFBC Support" OFF)
 | 
			
		||||
option(USE_D12   "Enable DirectX12 Support" ON)
 | 
			
		||||
option(USE_DXGI  "Enable DXGI Support" ON)
 | 
			
		||||
option(USE_NVFBC "Enable NVFBC Support" OFF)
 | 
			
		||||
 | 
			
		||||
if(NOT DEFINED NVFBC_SDK)
 | 
			
		||||
  set(NVFBC_SDK "C:/Program Files (x86)/NVIDIA Corporation/NVIDIA Capture SDK")
 | 
			
		||||
@@ -17,6 +18,10 @@ if(NOT EXISTS "${nvfbc_sdk}/inc" OR NOT IS_DIRECTORY "${nvfbc_sdk}/inc")
 | 
			
		||||
  set(USE_NVFBC OFF)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(USE_D12)
 | 
			
		||||
  add_capture("D12")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(USE_DXGI)
 | 
			
		||||
  add_capture("DXGI")
 | 
			
		||||
endif()
 | 
			
		||||
@@ -25,7 +30,8 @@ if(USE_NVFBC)
 | 
			
		||||
  add_capture("NVFBC")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
add_feature_info(USE_DXGI USE_DXGI "DXGI Desktop Duplication capture backend.")
 | 
			
		||||
add_feature_info(USE_D12   USE_D12   "DirectX12 capture backend.")
 | 
			
		||||
add_feature_info(USE_DXGI  USE_DXGI  "DXGI Desktop Duplication capture backend.")
 | 
			
		||||
add_feature_info(USE_NVFBC USE_NVFBC "NVFBC capture backend.")
 | 
			
		||||
 | 
			
		||||
include("PostCapture")
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								host/platform/Windows/capture/D12/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								host/platform/Windows/capture/D12/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
cmake_minimum_required(VERSION 3.5)
 | 
			
		||||
project(capture_D12 LANGUAGES C)
 | 
			
		||||
 | 
			
		||||
add_library(capture_D12 STATIC
 | 
			
		||||
  d12.c
 | 
			
		||||
  backend/dd.c
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_definitions("-DCOBJMACROS -DINITGUID -DWIDL_C_INLINE_WRAPPERS")
 | 
			
		||||
 | 
			
		||||
target_link_libraries(capture_D12
 | 
			
		||||
  lg_common
 | 
			
		||||
  d3d11
 | 
			
		||||
  dxgi
 | 
			
		||||
  dwmapi
 | 
			
		||||
  d3dcompiler
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
target_include_directories(capture_D12
 | 
			
		||||
  PRIVATE
 | 
			
		||||
    .
 | 
			
		||||
    "${PROJECT_TOP}/vendor/directx"
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										59
									
								
								host/platform/Windows/capture/D12/backend.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								host/platform/Windows/capture/D12/backend.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Looking Glass
 | 
			
		||||
 * Copyright © 2017-2024 The Looking Glass Authors
 | 
			
		||||
 * https://looking-glass.io
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms of the GNU General Public License as published by the Free
 | 
			
		||||
 * Software Foundation; either version 2 of the License, or (at your option)
 | 
			
		||||
 * any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 | 
			
		||||
 * more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License along
 | 
			
		||||
 * with this program; if not, write to the Free Software Foundation, Inc., 59
 | 
			
		||||
 * Temple Place, Suite 330, Boston, MA 02111-1307 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _H_D12_BACKEND_
 | 
			
		||||
#define _H_D12_BACKEND_
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <d3d12.h>
 | 
			
		||||
#include "interface/capture.h"
 | 
			
		||||
 | 
			
		||||
typedef struct D12Backend
 | 
			
		||||
{
 | 
			
		||||
  // friendly name
 | 
			
		||||
  const char * name;
 | 
			
		||||
 | 
			
		||||
  // internal name
 | 
			
		||||
  const char * codeName;
 | 
			
		||||
 | 
			
		||||
  // creation/init/free
 | 
			
		||||
  bool (*create)(unsigned frameBuffers);
 | 
			
		||||
  bool (*init)(
 | 
			
		||||
    bool                 debug,
 | 
			
		||||
    ID3D12Device3      * device,
 | 
			
		||||
    IDXGIAdapter1      * adapter,
 | 
			
		||||
    IDXGIOutput        * output);
 | 
			
		||||
  bool (*deinit)(void);
 | 
			
		||||
  void (*free)(void);
 | 
			
		||||
 | 
			
		||||
  // capture callbacks
 | 
			
		||||
  CaptureResult    (*capture)(unsigned frameBufferIndex);
 | 
			
		||||
  CaptureResult    (*sync   )(ID3D12CommandQueue * commandQueue);
 | 
			
		||||
  ID3D12Resource * (*fetch  )(unsigned frameBufferIndex);
 | 
			
		||||
}
 | 
			
		||||
D12Backend;
 | 
			
		||||
 | 
			
		||||
// apis for the backend
 | 
			
		||||
void d12_updatePointer(
 | 
			
		||||
  CapturePointer * pointer, void * shape, size_t shapeSize);
 | 
			
		||||
 | 
			
		||||
extern D12Backend D12Backend_DD;
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										712
									
								
								host/platform/Windows/capture/D12/backend/dd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										712
									
								
								host/platform/Windows/capture/D12/backend/dd.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,712 @@
 | 
			
		||||
#include "backend.h"
 | 
			
		||||
 | 
			
		||||
#include "com_ref.h"
 | 
			
		||||
#include "common/debug.h"
 | 
			
		||||
#include "common/windebug.h"
 | 
			
		||||
#include "common/array.h"
 | 
			
		||||
 | 
			
		||||
#include <d3d11.h>
 | 
			
		||||
#include <d3d11_4.h>
 | 
			
		||||
#include <d3d11on12.h>
 | 
			
		||||
 | 
			
		||||
#include <dxgi.h>
 | 
			
		||||
#include <dxgi1_2.h>
 | 
			
		||||
#include <dxgi1_3.h>
 | 
			
		||||
#include <dxgi1_5.h>
 | 
			
		||||
#include <dxgi1_6.h>
 | 
			
		||||
 | 
			
		||||
#define CACHE_SIZE 10
 | 
			
		||||
 | 
			
		||||
typedef struct DDCacheInfo
 | 
			
		||||
{
 | 
			
		||||
  D3D11_TEXTURE2D_DESC format;
 | 
			
		||||
  ID3D11Texture2D   ** srcTex;
 | 
			
		||||
  ID3D12Resource    ** d12Res;
 | 
			
		||||
  ID3D11Fence       ** fence;
 | 
			
		||||
  ID3D12Fence       ** d12Fence;
 | 
			
		||||
  UINT64               fenceValue;
 | 
			
		||||
  bool                 ready;
 | 
			
		||||
}
 | 
			
		||||
DDCacheInfo;
 | 
			
		||||
 | 
			
		||||
struct DDInstance
 | 
			
		||||
{
 | 
			
		||||
  ComScope * comScope;
 | 
			
		||||
 | 
			
		||||
  HDESK desktop;
 | 
			
		||||
 | 
			
		||||
  ID3D12Device3          ** d12device;
 | 
			
		||||
  ID3D11Device5          ** device;
 | 
			
		||||
  ID3D11DeviceContext4   ** context;
 | 
			
		||||
  IDXGIOutputDuplication ** dup;
 | 
			
		||||
  bool                      release;
 | 
			
		||||
 | 
			
		||||
  DDCacheInfo cache[CACHE_SIZE];
 | 
			
		||||
  DDCacheInfo * current;
 | 
			
		||||
 | 
			
		||||
  bool                          lastPosValid;
 | 
			
		||||
  DXGI_OUTDUPL_POINTER_POSITION lastPos;
 | 
			
		||||
 | 
			
		||||
  void   * shapeBuffer;
 | 
			
		||||
  unsigned shapeBufferSize;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct DDInstance * this = NULL;
 | 
			
		||||
 | 
			
		||||
#define comRef_toGlobal(dst, src) \
 | 
			
		||||
  _comRef_toGlobal(this->comScope, dst, src)
 | 
			
		||||
 | 
			
		||||
static void d12_dd_openDesktop(void);
 | 
			
		||||
static bool d12_dd_handleFrameUpdate(IDXGIResource * res);
 | 
			
		||||
 | 
			
		||||
static void d12_dd_handlePointerMovement(DXGI_OUTDUPL_POINTER_POSITION * pos,
 | 
			
		||||
  CapturePointer * pointer, bool * changed);
 | 
			
		||||
static void d12_dd_handlePointerShape(
 | 
			
		||||
  CapturePointer * pointer, size_t size, bool * changed);
 | 
			
		||||
 | 
			
		||||
static bool d12_dd_getCache(ID3D11Texture2D * srcTex, DDCacheInfo ** result);
 | 
			
		||||
static bool d12_dd_convertResource(ID3D11Texture2D * srcTex,
 | 
			
		||||
  DDCacheInfo * cache);
 | 
			
		||||
 | 
			
		||||
static bool d12_dd_create(unsigned frameBuffers)
 | 
			
		||||
{
 | 
			
		||||
  this = calloc(1, sizeof(*this));
 | 
			
		||||
  if (!this)
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_ERROR("out of memory");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool d12_dd_init(
 | 
			
		||||
  bool                 debug,
 | 
			
		||||
  ID3D12Device3      * device,
 | 
			
		||||
  IDXGIAdapter1      * adapter,
 | 
			
		||||
  IDXGIOutput        * output)
 | 
			
		||||
{
 | 
			
		||||
  bool result = false;
 | 
			
		||||
  HRESULT hr;
 | 
			
		||||
 | 
			
		||||
  comRef_initGlobalScope(10 + CACHE_SIZE * 2, this->comScope);
 | 
			
		||||
  comRef_scopePush(10);
 | 
			
		||||
 | 
			
		||||
  // try to open the desktop so we can capture the secure desktop
 | 
			
		||||
  d12_dd_openDesktop();
 | 
			
		||||
 | 
			
		||||
  comRef_defineLocal(IDXGIAdapter, _adapter);
 | 
			
		||||
  hr = IDXGIAdapter1_QueryInterface(
 | 
			
		||||
    adapter, &IID_IDXGIAdapter, (void **)_adapter);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_ERROR("Failed to get the IDXGIAdapter interface");
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static const D3D_FEATURE_LEVEL featureLevels[] =
 | 
			
		||||
  {
 | 
			
		||||
    D3D_FEATURE_LEVEL_12_1,
 | 
			
		||||
    D3D_FEATURE_LEVEL_12_0,
 | 
			
		||||
    D3D_FEATURE_LEVEL_11_1,
 | 
			
		||||
    D3D_FEATURE_LEVEL_11_0,
 | 
			
		||||
    D3D_FEATURE_LEVEL_10_1,
 | 
			
		||||
    D3D_FEATURE_LEVEL_10_0,
 | 
			
		||||
    D3D_FEATURE_LEVEL_9_3,
 | 
			
		||||
    D3D_FEATURE_LEVEL_9_2,
 | 
			
		||||
    D3D_FEATURE_LEVEL_9_1
 | 
			
		||||
  };
 | 
			
		||||
  D3D_FEATURE_LEVEL featureLevel;
 | 
			
		||||
 | 
			
		||||
  // create a DirectX11 context
 | 
			
		||||
  comRef_defineLocal(ID3D11Device       , d11device);
 | 
			
		||||
  comRef_defineLocal(ID3D11DeviceContext, d11context);
 | 
			
		||||
  hr = D3D11CreateDevice(
 | 
			
		||||
    *_adapter,
 | 
			
		||||
    D3D_DRIVER_TYPE_UNKNOWN,
 | 
			
		||||
    NULL,
 | 
			
		||||
    D3D11_CREATE_DEVICE_VIDEO_SUPPORT |
 | 
			
		||||
      (debug ? D3D11_CREATE_DEVICE_DEBUG : 0),
 | 
			
		||||
    featureLevels,
 | 
			
		||||
    ARRAY_LENGTH(featureLevels),
 | 
			
		||||
    D3D11_SDK_VERSION,
 | 
			
		||||
    d11device,
 | 
			
		||||
    &featureLevel,
 | 
			
		||||
    d11context);
 | 
			
		||||
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to create the D3D11Device", hr);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // get the updated interfaces
 | 
			
		||||
  comRef_defineLocal(ID3D11DeviceContext4, d11context4);
 | 
			
		||||
  hr = ID3D11DeviceContext_QueryInterface(
 | 
			
		||||
    *d11context, &IID_ID3D11DeviceContext4, (void **)d11context4);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to get the ID3D11Context4 interface", hr);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  comRef_defineLocal(ID3D11Device5, d11device5);
 | 
			
		||||
  hr = ID3D11Device_QueryInterface(
 | 
			
		||||
    *d11device, &IID_ID3D11Device5, (void **)d11device5);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to get the ID3D11Device5 interface", hr);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // try to reduce the latency
 | 
			
		||||
  comRef_defineLocal(IDXGIDevice1, dxgi1);
 | 
			
		||||
  hr = ID3D11Device_QueryInterface(
 | 
			
		||||
    *d11device, &IID_IDXGIDevice1, (void **)dxgi1);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("failed to query the DXGI interface from the device", hr);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
  IDXGIDevice1_SetMaximumFrameLatency(*dxgi1, 1);
 | 
			
		||||
 | 
			
		||||
  // duplicate the output
 | 
			
		||||
  comRef_defineLocal(IDXGIOutput5          , output5);
 | 
			
		||||
  comRef_defineLocal(IDXGIOutputDuplication, dup    );
 | 
			
		||||
  hr = IDXGIOutput_QueryInterface(output, &IID_IDXGIOutput5, (void **)output5);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WARN("IDXGIOutput5 is not available, "
 | 
			
		||||
               "please update windows for improved performance!");
 | 
			
		||||
    DEBUG_WARN("Falling back to IDXGIOutput1");
 | 
			
		||||
 | 
			
		||||
    comRef_defineLocal(IDXGIOutput1, output1);
 | 
			
		||||
    hr = IDXGIOutput_QueryInterface(output, &IID_IDXGIOutput1, (void **)output1);
 | 
			
		||||
    if (FAILED(hr))
 | 
			
		||||
    {
 | 
			
		||||
      DEBUG_ERROR("Failed to query IDXGIOutput1 from the output");
 | 
			
		||||
      goto exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // we try this twice in case we still get an error on re-initialization
 | 
			
		||||
    for (int i = 0; i < 2; ++i)
 | 
			
		||||
    {
 | 
			
		||||
      hr = IDXGIOutput1_DuplicateOutput(*output1, *(IUnknown **)d11device, dup);
 | 
			
		||||
      if (SUCCEEDED(hr))
 | 
			
		||||
        break;
 | 
			
		||||
      Sleep(200);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  else
 | 
			
		||||
  {
 | 
			
		||||
    static const DXGI_FORMAT supportedFormats[] =
 | 
			
		||||
    {
 | 
			
		||||
      DXGI_FORMAT_B8G8R8A8_UNORM,
 | 
			
		||||
      DXGI_FORMAT_R8G8B8A8_UNORM,
 | 
			
		||||
      DXGI_FORMAT_R16G16B16A16_FLOAT
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // we try this twice in case we still get an error on re-initialization
 | 
			
		||||
    for (int i = 0; i < 2; ++i)
 | 
			
		||||
    {
 | 
			
		||||
      hr = IDXGIOutput5_DuplicateOutput1(
 | 
			
		||||
        *output5,
 | 
			
		||||
        *(IUnknown **)d11device,
 | 
			
		||||
        0,
 | 
			
		||||
        ARRAY_LENGTH(supportedFormats),
 | 
			
		||||
        supportedFormats,
 | 
			
		||||
        dup);
 | 
			
		||||
 | 
			
		||||
      if (SUCCEEDED(hr))
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      // if access is denied we just keep trying until it isn't
 | 
			
		||||
      if (hr == E_ACCESSDENIED)
 | 
			
		||||
        --i;
 | 
			
		||||
 | 
			
		||||
      Sleep(200);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("DuplicateOutput Failed", hr);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ID3D12Device3_AddRef(device);
 | 
			
		||||
  comRef_toGlobal(this->d12device, &device    );
 | 
			
		||||
  comRef_toGlobal(this->device   , d11device5 );
 | 
			
		||||
  comRef_toGlobal(this->context  , d11context4);
 | 
			
		||||
  comRef_toGlobal(this->dup      , dup        );
 | 
			
		||||
  result = true;
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
  comRef_scopePop();
 | 
			
		||||
  if (!result)
 | 
			
		||||
    comRef_freeScope(&this->comScope);
 | 
			
		||||
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool d12_dd_deinit(void)
 | 
			
		||||
{
 | 
			
		||||
  if (this->release)
 | 
			
		||||
  {
 | 
			
		||||
    IDXGIOutputDuplication_ReleaseFrame(*this->dup);
 | 
			
		||||
    this->release = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->desktop)
 | 
			
		||||
  {
 | 
			
		||||
    CloseDesktop(this->desktop);
 | 
			
		||||
    this->desktop = NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  comRef_freeScope(&this->comScope);
 | 
			
		||||
  memset(this, 0, sizeof(*this));
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void d12_dd_free(void)
 | 
			
		||||
{
 | 
			
		||||
  free(this->shapeBuffer);
 | 
			
		||||
  free(this);
 | 
			
		||||
  this = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static CaptureResult d12_dd_hResultToCaptureResult(const HRESULT status)
 | 
			
		||||
{
 | 
			
		||||
  switch(status)
 | 
			
		||||
  {
 | 
			
		||||
    case S_OK:
 | 
			
		||||
      return CAPTURE_RESULT_OK;
 | 
			
		||||
 | 
			
		||||
    case DXGI_ERROR_WAIT_TIMEOUT:
 | 
			
		||||
      return CAPTURE_RESULT_TIMEOUT;
 | 
			
		||||
 | 
			
		||||
    case WAIT_ABANDONED:
 | 
			
		||||
    case DXGI_ERROR_ACCESS_LOST:
 | 
			
		||||
      return CAPTURE_RESULT_REINIT;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      return CAPTURE_RESULT_ERROR;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static CaptureResult d12_dd_capture(unsigned frameBufferIndex)
 | 
			
		||||
{
 | 
			
		||||
  HRESULT hr;
 | 
			
		||||
  CaptureResult result = CAPTURE_RESULT_ERROR;
 | 
			
		||||
  comRef_scopePush(10);
 | 
			
		||||
 | 
			
		||||
  DXGI_OUTDUPL_FRAME_INFO frameInfo = {0};
 | 
			
		||||
  comRef_defineLocal(IDXGIResource, res);
 | 
			
		||||
 | 
			
		||||
retry:
 | 
			
		||||
  if (this->release)
 | 
			
		||||
  {
 | 
			
		||||
    IDXGIOutputDuplication_ReleaseFrame(*this->dup);
 | 
			
		||||
    this->release = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  hr = IDXGIOutputDuplication_AcquireNextFrame(
 | 
			
		||||
    *this->dup, 1000, &frameInfo, res);
 | 
			
		||||
 | 
			
		||||
  result = d12_dd_hResultToCaptureResult(hr);
 | 
			
		||||
  if (result != CAPTURE_RESULT_OK)
 | 
			
		||||
  {
 | 
			
		||||
    if (result == CAPTURE_RESULT_ERROR)
 | 
			
		||||
      DEBUG_WINERROR("AcquireNextFrame failed", hr);
 | 
			
		||||
 | 
			
		||||
    if (hr == DXGI_ERROR_ACCESS_LOST)
 | 
			
		||||
    {
 | 
			
		||||
      hr = ID3D11Device5_GetDeviceRemovedReason(*this->device);
 | 
			
		||||
      if (FAILED(hr))
 | 
			
		||||
      {
 | 
			
		||||
        DEBUG_WINERROR("Device Removed", hr);
 | 
			
		||||
        result = CAPTURE_RESULT_ERROR;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->release = true;
 | 
			
		||||
 | 
			
		||||
  // if we have a new frame
 | 
			
		||||
  if (frameInfo.LastPresentTime.QuadPart != 0)
 | 
			
		||||
    if (!d12_dd_handleFrameUpdate(*res))
 | 
			
		||||
    {
 | 
			
		||||
      result = CAPTURE_RESULT_ERROR;
 | 
			
		||||
      goto exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  bool postPointer = false;
 | 
			
		||||
  bool postShape   = false;
 | 
			
		||||
  CapturePointer pointer = {0};
 | 
			
		||||
 | 
			
		||||
  // if the pointer has moved
 | 
			
		||||
  if (frameInfo.LastMouseUpdateTime.QuadPart != 0)
 | 
			
		||||
    d12_dd_handlePointerMovement(
 | 
			
		||||
      &frameInfo.PointerPosition, &pointer, &postPointer);
 | 
			
		||||
 | 
			
		||||
  // if the pointer shape has changed
 | 
			
		||||
  if (frameInfo.PointerShapeBufferSize > 0)
 | 
			
		||||
    d12_dd_handlePointerShape(
 | 
			
		||||
      &pointer, frameInfo.PointerShapeBufferSize, &postShape);
 | 
			
		||||
 | 
			
		||||
  if (postPointer)
 | 
			
		||||
    d12_updatePointer(&pointer, this->shapeBuffer, this->shapeBufferSize);
 | 
			
		||||
 | 
			
		||||
  // if this was not a frame update, go back and try again
 | 
			
		||||
  if (frameInfo.LastPresentTime.QuadPart == 0)
 | 
			
		||||
    goto retry;
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
  comRef_scopePop();
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static CaptureResult d12_dd_sync(ID3D12CommandQueue * commandQueue)
 | 
			
		||||
{
 | 
			
		||||
  if (!this->current)
 | 
			
		||||
    return CAPTURE_RESULT_TIMEOUT;
 | 
			
		||||
 | 
			
		||||
  DDCacheInfo * cache = this->current;
 | 
			
		||||
  if (ID3D11Fence_GetCompletedValue(*cache->fence) < cache->fenceValue)
 | 
			
		||||
    ID3D12CommandQueue_Wait(commandQueue, *cache->d12Fence, cache->fenceValue);
 | 
			
		||||
 | 
			
		||||
  return CAPTURE_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ID3D12Resource * d12_dd_fetch(unsigned frameBufferIndex)
 | 
			
		||||
{
 | 
			
		||||
  if (!this->current)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  ID3D12Resource_AddRef(*this->current->d12Res);
 | 
			
		||||
  return *this->current->d12Res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void d12_dd_openDesktop(void)
 | 
			
		||||
{
 | 
			
		||||
  this->desktop = OpenInputDesktop(0, FALSE, GENERIC_READ);
 | 
			
		||||
  if (!this->desktop)
 | 
			
		||||
    DEBUG_WINERROR("Failed to open the desktop", GetLastError());
 | 
			
		||||
  else
 | 
			
		||||
  {
 | 
			
		||||
    if (!SetThreadDesktop(this->desktop))
 | 
			
		||||
    {
 | 
			
		||||
      DEBUG_WINERROR("Failed to set the thread desktop", GetLastError());
 | 
			
		||||
      CloseDesktop(this->desktop);
 | 
			
		||||
      this->desktop = NULL;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!this->desktop)
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_INFO("The above error(s) will prevent LG from being able to capture "
 | 
			
		||||
               "the secure desktop (UAC dialogs)");
 | 
			
		||||
    DEBUG_INFO("This is not a failure, please do not report this as an issue.");
 | 
			
		||||
    DEBUG_INFO("To fix this, install and run the Looking Glass host as a "
 | 
			
		||||
               "service.");
 | 
			
		||||
    DEBUG_INFO("looking-glass-host.exe InstallService");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool d12_dd_handleFrameUpdate(IDXGIResource * res)
 | 
			
		||||
{
 | 
			
		||||
  bool result = false;
 | 
			
		||||
  comRef_scopePush(1);
 | 
			
		||||
 | 
			
		||||
  comRef_defineLocal(ID3D11Texture2D, srcTex);
 | 
			
		||||
  HRESULT hr = IDXGIResource_QueryInterface(
 | 
			
		||||
    res, &IID_ID3D11Texture2D, (void **)srcTex);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to obtain the D3D11Texture2D interface", hr);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!d12_dd_getCache(*srcTex, &this->current))
 | 
			
		||||
    goto exit;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Even though we have not performed any copy/draw operations we still need to
 | 
			
		||||
   * use a fence. Because we share this texture with DirectX12 it is able to
 | 
			
		||||
   * read from it before the desktop duplication API has finished updating it.*/
 | 
			
		||||
  ++this->current->fenceValue;
 | 
			
		||||
  ID3D11DeviceContext4_Signal(
 | 
			
		||||
    *this->context, *this->current->fence, this->current->fenceValue);
 | 
			
		||||
 | 
			
		||||
  result = true;
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
  comRef_scopePop();
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void d12_dd_handlePointerMovement(DXGI_OUTDUPL_POINTER_POSITION * pos,
 | 
			
		||||
  CapturePointer * pointer, bool * changed)
 | 
			
		||||
{
 | 
			
		||||
  bool setPos = false;
 | 
			
		||||
 | 
			
		||||
  // if the last position is valid, check against it for changes
 | 
			
		||||
  if (this->lastPosValid)
 | 
			
		||||
  {
 | 
			
		||||
    // update the position only if the pointer is visible and it has moved
 | 
			
		||||
    if (pos->Visible && (
 | 
			
		||||
        pos->Position.x != this->lastPos.Position.x ||
 | 
			
		||||
        pos->Position.y != this->lastPos.Position.y))
 | 
			
		||||
      setPos = true;
 | 
			
		||||
 | 
			
		||||
    // if the visibillity has changed
 | 
			
		||||
    if (pos->Visible != this->lastPos.Visible)
 | 
			
		||||
      *changed = true;
 | 
			
		||||
  }
 | 
			
		||||
  else
 | 
			
		||||
  {
 | 
			
		||||
    // update the position only if the pointer is visible
 | 
			
		||||
    setPos   = pos->Visible;
 | 
			
		||||
 | 
			
		||||
    // this is the first update, we need to send it
 | 
			
		||||
    *changed = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pointer->visible = pos->Visible;
 | 
			
		||||
  if (setPos)
 | 
			
		||||
  {
 | 
			
		||||
    pointer->positionUpdate = true;
 | 
			
		||||
    pointer->x              = pos->Position.x;
 | 
			
		||||
    pointer->y              = pos->Position.y;
 | 
			
		||||
 | 
			
		||||
    *changed = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  memcpy(&this->lastPos, pos, sizeof(*pos));
 | 
			
		||||
  this->lastPosValid = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void d12_dd_handlePointerShape(
 | 
			
		||||
  CapturePointer * pointer, size_t size, bool * changed)
 | 
			
		||||
{
 | 
			
		||||
  HRESULT hr;
 | 
			
		||||
  DXGI_OUTDUPL_POINTER_SHAPE_INFO info;
 | 
			
		||||
 | 
			
		||||
retry:
 | 
			
		||||
  if (this->shapeBufferSize < size)
 | 
			
		||||
  {
 | 
			
		||||
    free(this->shapeBuffer);
 | 
			
		||||
    this->shapeBuffer = malloc(size);
 | 
			
		||||
    if (!this->shapeBuffer)
 | 
			
		||||
    {
 | 
			
		||||
      DEBUG_ERROR("out of memory");
 | 
			
		||||
      this->shapeBufferSize = 0;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this->shapeBufferSize = size;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  UINT s;
 | 
			
		||||
  hr = IDXGIOutputDuplication_GetFramePointerShape(
 | 
			
		||||
    *this->dup,
 | 
			
		||||
    this->shapeBufferSize,
 | 
			
		||||
    this->shapeBuffer,
 | 
			
		||||
    &s,
 | 
			
		||||
    &info);
 | 
			
		||||
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    if (hr == DXGI_ERROR_MORE_DATA)
 | 
			
		||||
    {
 | 
			
		||||
      size = s;
 | 
			
		||||
      goto retry;
 | 
			
		||||
    }
 | 
			
		||||
    DEBUG_WINERROR("Failed to get the pointer shape", hr);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  switch(info.Type)
 | 
			
		||||
  {
 | 
			
		||||
    case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
 | 
			
		||||
      pointer->format = CAPTURE_FMT_COLOR;
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
 | 
			
		||||
      pointer->format = CAPTURE_FMT_MASKED;
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME:
 | 
			
		||||
      pointer->format = CAPTURE_FMT_MONO;
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      DEBUG_ERROR("Unsupporter cursor format");
 | 
			
		||||
      return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pointer->shapeUpdate = true;
 | 
			
		||||
  pointer->width       = info.Width;
 | 
			
		||||
  pointer->height      = info.Height;
 | 
			
		||||
  pointer->pitch       = info.Pitch;
 | 
			
		||||
  pointer->hx          = info.HotSpot.x;
 | 
			
		||||
  pointer->hy          = info.HotSpot.y;
 | 
			
		||||
 | 
			
		||||
  *changed = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool d12_dd_getCache(ID3D11Texture2D * srcTex, DDCacheInfo ** result)
 | 
			
		||||
{
 | 
			
		||||
  *result = NULL;
 | 
			
		||||
  D3D11_TEXTURE2D_DESC srcDesc;
 | 
			
		||||
  ID3D11Texture2D_GetDesc(srcTex, &srcDesc);
 | 
			
		||||
 | 
			
		||||
  unsigned freeSlot = CACHE_SIZE;
 | 
			
		||||
  for(unsigned i = 0; i < CACHE_SIZE; ++i)
 | 
			
		||||
  {
 | 
			
		||||
    DDCacheInfo * cache = &this->cache[i];
 | 
			
		||||
    if (!cache->ready)
 | 
			
		||||
    {
 | 
			
		||||
      freeSlot = min(freeSlot, i);
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check for a resource match
 | 
			
		||||
    if (*cache->srcTex != srcTex)
 | 
			
		||||
      continue;
 | 
			
		||||
 | 
			
		||||
    // check if the match is not valid
 | 
			
		||||
    if (cache->format.Width  != srcDesc.Width  ||
 | 
			
		||||
        cache->format.Height != srcDesc.Height ||
 | 
			
		||||
        cache->format.Format != srcDesc.Format)
 | 
			
		||||
    {
 | 
			
		||||
      // break out and allow this entry to be rebuilt
 | 
			
		||||
      cache->ready = false;
 | 
			
		||||
      freeSlot = i;
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // found, so return it
 | 
			
		||||
    *result = cache;
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // cache is full
 | 
			
		||||
  if (freeSlot == CACHE_SIZE)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  // convert the resource
 | 
			
		||||
  if (!d12_dd_convertResource(srcTex, &this->cache[freeSlot]))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  // return the new cache entry
 | 
			
		||||
  *result = &this->cache[freeSlot];
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool d12_dd_convertResource(ID3D11Texture2D * srcTex, DDCacheInfo * cache)
 | 
			
		||||
{
 | 
			
		||||
  bool result = false;
 | 
			
		||||
  HRESULT hr;
 | 
			
		||||
  comRef_scopePush(10);
 | 
			
		||||
 | 
			
		||||
  D3D11_TEXTURE2D_DESC srcDesc;
 | 
			
		||||
  ID3D11Texture2D_GetDesc(srcTex, &srcDesc);
 | 
			
		||||
 | 
			
		||||
  // get the DXGI resource interface so we can create the shared handle
 | 
			
		||||
  comRef_defineLocal(IDXGIResource1, dxgiRes);
 | 
			
		||||
  hr = ID3D11Texture2D_QueryInterface(
 | 
			
		||||
    srcTex, &IID_IDXGIResource1, (void **)dxgiRes);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to obtain the shared ID3D11Resource1 interface", hr);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // create the shared handle
 | 
			
		||||
  HANDLE sharedHandle;
 | 
			
		||||
  hr = IDXGIResource1_CreateSharedHandle(
 | 
			
		||||
    *dxgiRes, NULL, DXGI_SHARED_RESOURCE_READ, NULL, &sharedHandle);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to create the shared handle", hr);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // open the resource as a DirectX12 resource
 | 
			
		||||
  comRef_defineLocal(ID3D12Resource, dst);
 | 
			
		||||
  hr = ID3D12Device3_OpenSharedHandle(
 | 
			
		||||
    *this->d12device, sharedHandle, &IID_ID3D12Resource, (void **)dst);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to open the D3D12Resource from the handle", hr);
 | 
			
		||||
    CloseHandle(sharedHandle);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // close the shared handle
 | 
			
		||||
  CloseHandle(sharedHandle);
 | 
			
		||||
 | 
			
		||||
  // create the sync fence
 | 
			
		||||
  comRef_defineLocal(ID3D11Fence, fence);
 | 
			
		||||
  hr = ID3D11Device5_CreateFence(
 | 
			
		||||
    *this->device, 0, D3D11_FENCE_FLAG_SHARED, &IID_ID3D11Fence, (void **)fence);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to create the fence", hr);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // create the fence shared handle
 | 
			
		||||
  hr = ID3D11Fence_CreateSharedHandle(
 | 
			
		||||
    *fence, NULL, GENERIC_ALL, NULL, &sharedHandle);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to create the fence shared handle", hr);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // open the fence as a DirectX12 fence
 | 
			
		||||
  comRef_defineLocal(ID3D12Fence, d12Fence);
 | 
			
		||||
  hr = ID3D12Device3_OpenSharedHandle(
 | 
			
		||||
    *this->d12device, sharedHandle, &IID_ID3D12Fence, (void **)d12Fence);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to open the D3D12Fence from the handle", hr);
 | 
			
		||||
    CloseHandle(sharedHandle);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // close the shared handle
 | 
			
		||||
  CloseHandle(sharedHandle);
 | 
			
		||||
 | 
			
		||||
  // store the details
 | 
			
		||||
  ID3D11Texture2D_AddRef(srcTex);
 | 
			
		||||
  comRef_toGlobal(cache->srcTex  , &srcTex );
 | 
			
		||||
  comRef_toGlobal(cache->d12Res  , dst     );
 | 
			
		||||
  comRef_toGlobal(cache->fence   , fence   );
 | 
			
		||||
  comRef_toGlobal(cache->d12Fence, d12Fence);
 | 
			
		||||
  memcpy(&cache->format, &srcDesc, sizeof(srcDesc));
 | 
			
		||||
  cache->fenceValue = 0;
 | 
			
		||||
  cache->ready      = true;
 | 
			
		||||
 | 
			
		||||
  result = true;
 | 
			
		||||
exit:
 | 
			
		||||
  comRef_scopePop();
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
D12Backend D12Backend_DD =
 | 
			
		||||
{
 | 
			
		||||
  .name     = "Desktop Duplication",
 | 
			
		||||
  .codeName = "DD",
 | 
			
		||||
 | 
			
		||||
  .create   = d12_dd_create,
 | 
			
		||||
  .init     = d12_dd_init,
 | 
			
		||||
  .deinit   = d12_dd_deinit,
 | 
			
		||||
  .free     = d12_dd_free,
 | 
			
		||||
  .capture  = d12_dd_capture,
 | 
			
		||||
  .sync     = d12_dd_sync,
 | 
			
		||||
  .fetch    = d12_dd_fetch
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										743
									
								
								host/platform/Windows/capture/D12/d12.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										743
									
								
								host/platform/Windows/capture/D12/d12.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,743 @@
 | 
			
		||||
#include "interface/capture.h"
 | 
			
		||||
 | 
			
		||||
#include "common/array.h"
 | 
			
		||||
#include "common/debug.h"
 | 
			
		||||
#include "common/windebug.h"
 | 
			
		||||
#include "com_ref.h"
 | 
			
		||||
 | 
			
		||||
#include "backend.h"
 | 
			
		||||
 | 
			
		||||
#include <dxgi.h>
 | 
			
		||||
#include <dxgi1_3.h>
 | 
			
		||||
#include <d3dcommon.h>
 | 
			
		||||
 | 
			
		||||
// definitions
 | 
			
		||||
 | 
			
		||||
typedef HRESULT (*D3D12CreateDevice_t)(
 | 
			
		||||
  IUnknown          *pAdapter,
 | 
			
		||||
  D3D_FEATURE_LEVEL MinimumFeatureLevel,
 | 
			
		||||
  REFIID            riid,
 | 
			
		||||
  void              **ppDevice
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
typedef HRESULT (*D3D12GetDebugInterface_t)(
 | 
			
		||||
  REFIID riid,
 | 
			
		||||
  void   **ppvDebug
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
typedef struct D12CommandGroup
 | 
			
		||||
{
 | 
			
		||||
  ID3D12CommandAllocator    ** allocator;
 | 
			
		||||
  ID3D12GraphicsCommandList ** gfxList;
 | 
			
		||||
  ID3D12CommandList         ** cmdList;
 | 
			
		||||
  ID3D12Fence               ** fence;
 | 
			
		||||
  HANDLE                       event;
 | 
			
		||||
  UINT64                       fenceValue;
 | 
			
		||||
}
 | 
			
		||||
D12CommandGroup;
 | 
			
		||||
 | 
			
		||||
struct D12Interface
 | 
			
		||||
{
 | 
			
		||||
  ComScope * comScope;
 | 
			
		||||
 | 
			
		||||
  HMODULE                  d3d12;
 | 
			
		||||
  D3D12CreateDevice_t      D3D12CreateDevice;
 | 
			
		||||
  D3D12GetDebugInterface_t D3D12GetDebugInterface;
 | 
			
		||||
 | 
			
		||||
  IDXGIFactory2      ** factory;
 | 
			
		||||
  ID3D12Device3      ** device;
 | 
			
		||||
 | 
			
		||||
  ID3D12CommandQueue ** commandQueue;
 | 
			
		||||
  D12CommandGroup       copyCommand;
 | 
			
		||||
 | 
			
		||||
  void        * ivshmemBase;
 | 
			
		||||
  ID3D12Heap ** ivshmemHeap;
 | 
			
		||||
 | 
			
		||||
  CaptureGetPointerBuffer  getPointerBufferFn;
 | 
			
		||||
  CapturePostPointerBuffer postPointerBufferFn;
 | 
			
		||||
 | 
			
		||||
  D12Backend * backend;
 | 
			
		||||
 | 
			
		||||
  // capture format tracking
 | 
			
		||||
  D3D12_RESOURCE_DESC lastFormat;
 | 
			
		||||
  unsigned            formatVer;
 | 
			
		||||
 | 
			
		||||
  // options
 | 
			
		||||
  bool debug;
 | 
			
		||||
 | 
			
		||||
  // must be last
 | 
			
		||||
  struct
 | 
			
		||||
  {
 | 
			
		||||
    // the size of the frame buffer
 | 
			
		||||
    unsigned          size;
 | 
			
		||||
    // the frame buffer it itself
 | 
			
		||||
    FrameBuffer    *  frameBuffer;
 | 
			
		||||
    // the resource backed by the framebuffer
 | 
			
		||||
    ID3D12Resource ** resource;
 | 
			
		||||
  }
 | 
			
		||||
  frameBuffers[0];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// defines
 | 
			
		||||
 | 
			
		||||
#define comRef_toGlobal(dst, src) \
 | 
			
		||||
  _comRef_toGlobal(this->comScope, dst, src)
 | 
			
		||||
 | 
			
		||||
// locals
 | 
			
		||||
 | 
			
		||||
static struct D12Interface * this = NULL;
 | 
			
		||||
 | 
			
		||||
// forwards
 | 
			
		||||
 | 
			
		||||
static bool d12_enumerateDevices(
 | 
			
		||||
  IDXGIFactory2 ** factory,
 | 
			
		||||
  IDXGIAdapter1 ** adapter,
 | 
			
		||||
  IDXGIOutput   ** output);
 | 
			
		||||
 | 
			
		||||
static bool d12_createCommandGroup(
 | 
			
		||||
  ID3D12Device3            * device,
 | 
			
		||||
  D3D12_COMMAND_LIST_TYPE    type,
 | 
			
		||||
  D12CommandGroup          * dst,
 | 
			
		||||
  LPCWSTR name);
 | 
			
		||||
 | 
			
		||||
static void d12_freeCommandGroup(
 | 
			
		||||
  D12CommandGroup * grp);
 | 
			
		||||
 | 
			
		||||
static bool d12_executeCommandGroup(
 | 
			
		||||
  D12CommandGroup * grp);
 | 
			
		||||
 | 
			
		||||
static ID3D12Resource * d12_frameBufferToResource(
 | 
			
		||||
  unsigned      frameBufferIndex,
 | 
			
		||||
  FrameBuffer * frameBuffer,
 | 
			
		||||
  unsigned size);
 | 
			
		||||
 | 
			
		||||
// implementation
 | 
			
		||||
 | 
			
		||||
static const char * d12_getName(void)
 | 
			
		||||
{
 | 
			
		||||
  return "D12";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void d12_initOptions(void)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool d12_create(
 | 
			
		||||
  CaptureGetPointerBuffer  getPointerBufferFn,
 | 
			
		||||
  CapturePostPointerBuffer postPointerBufferFn,
 | 
			
		||||
  unsigned                 frameBuffers)
 | 
			
		||||
{
 | 
			
		||||
  this = calloc(1, offsetof(struct D12Interface, frameBuffers) +
 | 
			
		||||
    sizeof(this->frameBuffers[0]) * frameBuffers);
 | 
			
		||||
  if (!this)
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_ERROR("failed to allocate D12Interface struct");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->debug = false;
 | 
			
		||||
  this->d3d12 = LoadLibrary("d3d12.dll");
 | 
			
		||||
  if (!this->d3d12)
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_ERROR("failed to load d3d12.dll");
 | 
			
		||||
    free(this);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->D3D12CreateDevice = (D3D12CreateDevice_t)
 | 
			
		||||
    GetProcAddress(this->d3d12, "D3D12CreateDevice");
 | 
			
		||||
 | 
			
		||||
  this->D3D12GetDebugInterface = (D3D12GetDebugInterface_t)
 | 
			
		||||
    GetProcAddress(this->d3d12, "D3D12GetDebugInterface");
 | 
			
		||||
 | 
			
		||||
  this->getPointerBufferFn  = getPointerBufferFn;
 | 
			
		||||
  this->postPointerBufferFn = postPointerBufferFn;
 | 
			
		||||
 | 
			
		||||
  this->backend = &D12Backend_DD;
 | 
			
		||||
  if (!this->backend->create(frameBuffers))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_ERROR("backend \"%s\" failed to create", this->backend->codeName);
 | 
			
		||||
    CloseHandle(this->d3d12);
 | 
			
		||||
    free(this);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool d12_init(void * ivshmemBase, unsigned * alignSize)
 | 
			
		||||
{
 | 
			
		||||
  bool result = false;
 | 
			
		||||
  comRef_initGlobalScope(100, this->comScope);
 | 
			
		||||
  comRef_scopePush(10);
 | 
			
		||||
 | 
			
		||||
  // create a DXGI factory
 | 
			
		||||
  comRef_defineLocal(IDXGIFactory2, factory);
 | 
			
		||||
  HRESULT hr = CreateDXGIFactory2(
 | 
			
		||||
    this->debug ? DXGI_CREATE_FACTORY_DEBUG : 0,
 | 
			
		||||
    &IID_IDXGIFactory2,
 | 
			
		||||
    (void **)factory);
 | 
			
		||||
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to create the DXGI factory", hr);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // find the adapter and output we want to use
 | 
			
		||||
  comRef_defineLocal(IDXGIAdapter1, adapter);
 | 
			
		||||
  comRef_defineLocal(IDXGIOutput  , output );
 | 
			
		||||
  if (!d12_enumerateDevices(factory, adapter, output))
 | 
			
		||||
    goto exit;
 | 
			
		||||
 | 
			
		||||
  if (this->debug)
 | 
			
		||||
  {
 | 
			
		||||
    comRef_defineLocal(ID3D12Debug1, debug);
 | 
			
		||||
    hr = this->D3D12GetDebugInterface(&IID_ID3D12Debug1, (void **)debug);
 | 
			
		||||
    if (FAILED(hr))
 | 
			
		||||
    {
 | 
			
		||||
      DEBUG_WINERROR("D3D12GetDebugInterface", hr);
 | 
			
		||||
      goto exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ID3D12Debug1_EnableDebugLayer(*debug);
 | 
			
		||||
    ID3D12Debug1_SetEnableGPUBasedValidation(*debug, TRUE);
 | 
			
		||||
    ID3D12Debug1_SetEnableSynchronizedCommandQueueValidation(*debug, TRUE);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // create the D3D12 device
 | 
			
		||||
  comRef_defineLocal(ID3D12Device3, device);
 | 
			
		||||
  hr = this->D3D12CreateDevice(
 | 
			
		||||
    (IUnknown *)*adapter,
 | 
			
		||||
    D3D_FEATURE_LEVEL_12_0,
 | 
			
		||||
    &IID_ID3D12Device3,
 | 
			
		||||
    (void **)device);
 | 
			
		||||
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to create the DirectX12 device", hr);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* make this static as we downgrade the priority on failure and we want to
 | 
			
		||||
  remember it */
 | 
			
		||||
  static D3D12_COMMAND_QUEUE_DESC queueDesc =
 | 
			
		||||
  {
 | 
			
		||||
    .Type     = D3D12_COMMAND_LIST_TYPE_COPY,
 | 
			
		||||
    .Priority = D3D12_COMMAND_QUEUE_PRIORITY_GLOBAL_REALTIME,
 | 
			
		||||
    .Flags    = D3D12_COMMAND_QUEUE_FLAG_NONE,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  comRef_defineLocal(ID3D12CommandQueue, commandQueue);
 | 
			
		||||
retryCreateCommandQueue:
 | 
			
		||||
  hr = ID3D12Device3_CreateCommandQueue(
 | 
			
		||||
    *device, &queueDesc, &IID_ID3D12CommandQueue, (void **)commandQueue);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    if (queueDesc.Priority == D3D12_COMMAND_QUEUE_PRIORITY_GLOBAL_REALTIME)
 | 
			
		||||
    {
 | 
			
		||||
      DEBUG_WARN("Failed to create queue with real time priority");
 | 
			
		||||
      queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_HIGH;
 | 
			
		||||
      goto retryCreateCommandQueue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DEBUG_WINERROR("Failed to create ID3D12CommandQueue", hr);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
  ID3D12CommandQueue_SetName(*commandQueue, L"Command Queue");
 | 
			
		||||
 | 
			
		||||
  if (!d12_createCommandGroup(
 | 
			
		||||
    *device, D3D12_COMMAND_LIST_TYPE_COPY, &this->copyCommand, L"Copy"))
 | 
			
		||||
    goto exit;
 | 
			
		||||
 | 
			
		||||
  // Create the IVSHMEM heap
 | 
			
		||||
  this->ivshmemBase = ivshmemBase;
 | 
			
		||||
  comRef_defineLocal(ID3D12Heap, ivshmemHeap);
 | 
			
		||||
  hr = ID3D12Device3_OpenExistingHeapFromAddress(
 | 
			
		||||
    *device, ivshmemBase, &IID_ID3D12Heap, (void **)ivshmemHeap);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to open the framebuffer as a D3D12Heap", hr);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Adjust the alignSize based on the required heap alignment
 | 
			
		||||
  D3D12_HEAP_DESC heapDesc = ID3D12Heap_GetDesc(*ivshmemHeap);
 | 
			
		||||
  *alignSize = heapDesc.Alignment;
 | 
			
		||||
 | 
			
		||||
  // initialize the backend
 | 
			
		||||
  if (!this->backend->init(this->debug, *device, *adapter, *output))
 | 
			
		||||
    goto exit;
 | 
			
		||||
 | 
			
		||||
  comRef_toGlobal(this->factory     , factory     );
 | 
			
		||||
  comRef_toGlobal(this->device      , device      );
 | 
			
		||||
  comRef_toGlobal(this->commandQueue, commandQueue);
 | 
			
		||||
  comRef_toGlobal(this->ivshmemHeap , ivshmemHeap );
 | 
			
		||||
 | 
			
		||||
  result = true;
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
  comRef_scopePop();
 | 
			
		||||
  if (!result)
 | 
			
		||||
    comRef_freeScope(&this->comScope);
 | 
			
		||||
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void d12_stop(void)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool d12_deinit(void)
 | 
			
		||||
{
 | 
			
		||||
  bool result = true;
 | 
			
		||||
  if (!this->backend->deinit())
 | 
			
		||||
    result = false;
 | 
			
		||||
 | 
			
		||||
  d12_freeCommandGroup(&this->copyCommand);
 | 
			
		||||
  comRef_freeScope(&this->comScope);
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void d12_free(void)
 | 
			
		||||
{
 | 
			
		||||
  this->backend->free();
 | 
			
		||||
  FreeLibrary(this->d3d12);
 | 
			
		||||
  free(this);
 | 
			
		||||
  this = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static CaptureResult d12_capture(
 | 
			
		||||
  unsigned frameBufferIndex, FrameBuffer * frameBuffer)
 | 
			
		||||
{
 | 
			
		||||
  return this->backend->capture(frameBufferIndex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static CaptureResult d12_waitFrame(unsigned frameBufferIndex,
 | 
			
		||||
  CaptureFrame * frame, const size_t maxFrameSize)
 | 
			
		||||
{
 | 
			
		||||
  CaptureResult result = CAPTURE_RESULT_ERROR;
 | 
			
		||||
  comRef_scopePush(1);
 | 
			
		||||
 | 
			
		||||
  comRef_defineLocal(ID3D12Resource, src);
 | 
			
		||||
  *src = this->backend->fetch(frameBufferIndex);
 | 
			
		||||
  if (!*src)
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_ERROR("D12 backend failed to produce an expected frame: %u",
 | 
			
		||||
      frameBufferIndex);
 | 
			
		||||
    result = CAPTURE_RESULT_ERROR;
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  D3D12_RESOURCE_DESC desc = ID3D12Resource_GetDesc(*src);
 | 
			
		||||
  if (desc.Width != this->lastFormat.Width ||
 | 
			
		||||
      desc.Height != this->lastFormat.Height ||
 | 
			
		||||
      desc.Format != this->lastFormat.Format)
 | 
			
		||||
  {
 | 
			
		||||
    ++this->formatVer;
 | 
			
		||||
    memcpy(&this->lastFormat, &desc, sizeof(desc));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const unsigned int maxRows = maxFrameSize / (desc.Width * 4);
 | 
			
		||||
 | 
			
		||||
  frame->formatVer        = this->formatVer;
 | 
			
		||||
  frame->screenWidth      = desc.Width;
 | 
			
		||||
  frame->screenHeight     = desc.Height;
 | 
			
		||||
  frame->dataWidth        = desc.Width;
 | 
			
		||||
  frame->dataHeight       = min(maxRows, desc.Height);
 | 
			
		||||
  frame->frameWidth       = desc.Width;
 | 
			
		||||
  frame->frameHeight      = desc.Height;
 | 
			
		||||
  frame->truncated        = maxRows < desc.Height;
 | 
			
		||||
  frame->pitch            = desc.Width * 4;
 | 
			
		||||
  frame->stride           = desc.Width;
 | 
			
		||||
  frame->format           = CAPTURE_FMT_BGRA;
 | 
			
		||||
  frame->hdr              = false;
 | 
			
		||||
  frame->hdrPQ            = false;
 | 
			
		||||
  frame->rotation         = CAPTURE_ROT_0;
 | 
			
		||||
  frame->damageRectsCount = 0;
 | 
			
		||||
 | 
			
		||||
  result = CAPTURE_RESULT_OK;
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
  comRef_scopePop();
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static CaptureResult d12_getFrame(unsigned frameBufferIndex,
 | 
			
		||||
  FrameBuffer * frameBuffer, const size_t maxFrameSize)
 | 
			
		||||
{
 | 
			
		||||
  CaptureResult result = CAPTURE_RESULT_ERROR;
 | 
			
		||||
  comRef_scopePush(2);
 | 
			
		||||
 | 
			
		||||
  comRef_defineLocal(ID3D12Resource, src);
 | 
			
		||||
  *src = this->backend->fetch(frameBufferIndex);
 | 
			
		||||
  if (!*src)
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_ERROR("D12 backend failed to produce an expected frame: %u",
 | 
			
		||||
      frameBufferIndex);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  comRef_defineLocal(ID3D12Resource, dst)
 | 
			
		||||
  *dst = d12_frameBufferToResource(frameBufferIndex, frameBuffer, maxFrameSize);
 | 
			
		||||
  if (!*dst)
 | 
			
		||||
    goto exit;
 | 
			
		||||
 | 
			
		||||
  // copy into the framebuffer resource
 | 
			
		||||
  D3D12_RESOURCE_DESC desc = ID3D12Resource_GetDesc(*src);
 | 
			
		||||
  D3D12_TEXTURE_COPY_LOCATION srcLoc =
 | 
			
		||||
  {
 | 
			
		||||
    .pResource        = *src,
 | 
			
		||||
    .Type             = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
 | 
			
		||||
    .SubresourceIndex = 0
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  D3D12_TEXTURE_COPY_LOCATION dstLoc =
 | 
			
		||||
  {
 | 
			
		||||
    .pResource       = *dst,
 | 
			
		||||
    .Type            = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
 | 
			
		||||
    .PlacedFootprint =
 | 
			
		||||
    {
 | 
			
		||||
      .Offset = 0,
 | 
			
		||||
      .Footprint =
 | 
			
		||||
      {
 | 
			
		||||
        .Format   = desc.Format,
 | 
			
		||||
        .Width    = desc.Width,
 | 
			
		||||
        .Height   = desc.Height,
 | 
			
		||||
        .Depth    = 1,
 | 
			
		||||
        .RowPitch = desc.Width * 4
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  ID3D12GraphicsCommandList_CopyTextureRegion(
 | 
			
		||||
    *this->copyCommand.gfxList, &dstLoc, 0, 0, 0, &srcLoc, NULL);
 | 
			
		||||
 | 
			
		||||
  // allow the backend to insert a fence into the command queue if it needs it
 | 
			
		||||
  result = this->backend->sync(*this->commandQueue);
 | 
			
		||||
  if (result != CAPTURE_RESULT_OK)
 | 
			
		||||
    goto exit;
 | 
			
		||||
 | 
			
		||||
  d12_executeCommandGroup(&this->copyCommand);
 | 
			
		||||
 | 
			
		||||
  framebuffer_set_write_ptr(frameBuffer, desc.Height * desc.Width * 4);
 | 
			
		||||
  result = CAPTURE_RESULT_OK;
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
  comRef_scopePop();
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool d12_enumerateDevices(
 | 
			
		||||
  IDXGIFactory2 ** factory,
 | 
			
		||||
  IDXGIAdapter1 ** adapter,
 | 
			
		||||
  IDXGIOutput   ** output)
 | 
			
		||||
{
 | 
			
		||||
  DXGI_ADAPTER_DESC1 adapterDesc;
 | 
			
		||||
  DXGI_OUTPUT_DESC   outputDesc;
 | 
			
		||||
 | 
			
		||||
  for(
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    IDXGIFactory2_EnumAdapters1(*factory, i, adapter)
 | 
			
		||||
      != DXGI_ERROR_NOT_FOUND;
 | 
			
		||||
    ++i, comRef_release(adapter))
 | 
			
		||||
  {
 | 
			
		||||
    HRESULT hr = IDXGIAdapter1_GetDesc1(*adapter, &adapterDesc);
 | 
			
		||||
    if (FAILED(hr))
 | 
			
		||||
    {
 | 
			
		||||
      DEBUG_WINERROR("Failed to get the device description", hr);
 | 
			
		||||
      comRef_release(adapter);
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check for devices without D3D support
 | 
			
		||||
    static const UINT blacklist[][2] =
 | 
			
		||||
    {
 | 
			
		||||
      //VID  , PID
 | 
			
		||||
      {0x1414, 0x008c}, // Microsoft Basic Render Driver
 | 
			
		||||
      {0x1b36, 0x000d}, // QXL
 | 
			
		||||
      {0x1234, 0x1111}  // QEMU Standard VGA
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    bool skip = false;
 | 
			
		||||
    for(int n = 0; n < ARRAY_LENGTH(blacklist); ++n)
 | 
			
		||||
    {
 | 
			
		||||
      if (adapterDesc.VendorId == blacklist[n][0] &&
 | 
			
		||||
          adapterDesc.DeviceId == blacklist[n][1])
 | 
			
		||||
      {
 | 
			
		||||
        DEBUG_INFO("Not using unsupported adapter: %ls",
 | 
			
		||||
          adapterDesc.Description);
 | 
			
		||||
        skip = true;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (skip)
 | 
			
		||||
      continue;
 | 
			
		||||
 | 
			
		||||
    // FIXME: Allow specifying the specific adapter
 | 
			
		||||
 | 
			
		||||
    for(
 | 
			
		||||
      int n = 0;
 | 
			
		||||
      IDXGIAdapter1_EnumOutputs(*adapter, n, output) != DXGI_ERROR_NOT_FOUND;
 | 
			
		||||
      ++n, comRef_release(output))
 | 
			
		||||
    {
 | 
			
		||||
      IDXGIOutput_GetDesc(*output, &outputDesc);
 | 
			
		||||
      // FIXME: Allow specifying the specific output
 | 
			
		||||
 | 
			
		||||
      if (outputDesc.AttachedToDesktop)
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (*output)
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!*output)
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_ERROR("Failed to locate a valid output device");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  DEBUG_INFO("Device Name       : %ls"    , outputDesc.DeviceName);
 | 
			
		||||
  DEBUG_INFO("Device Description: %ls"    , adapterDesc.Description);
 | 
			
		||||
  DEBUG_INFO("Device Vendor ID  : 0x%x"   , adapterDesc.VendorId);
 | 
			
		||||
  DEBUG_INFO("Device Device ID  : 0x%x"   , adapterDesc.DeviceId);
 | 
			
		||||
  DEBUG_INFO("Device Video Mem  : %u MiB" ,
 | 
			
		||||
    (unsigned)(adapterDesc.DedicatedVideoMemory  / 1048576));
 | 
			
		||||
  DEBUG_INFO("Device Sys Mem    : %u MiB" ,
 | 
			
		||||
    (unsigned)(adapterDesc.DedicatedSystemMemory / 1048576));
 | 
			
		||||
  DEBUG_INFO("Shared Sys Mem    : %u MiB" ,
 | 
			
		||||
    (unsigned)(adapterDesc.SharedSystemMemory    / 1048576));
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool d12_createCommandGroup(
 | 
			
		||||
  ID3D12Device3            * device,
 | 
			
		||||
  D3D12_COMMAND_LIST_TYPE    type,
 | 
			
		||||
  D12CommandGroup          * dst,
 | 
			
		||||
  LPCWSTR name)
 | 
			
		||||
{
 | 
			
		||||
  bool result = false;
 | 
			
		||||
  HRESULT hr;
 | 
			
		||||
  comRef_scopePush(10);
 | 
			
		||||
 | 
			
		||||
  comRef_defineLocal(ID3D12CommandAllocator, allocator);
 | 
			
		||||
  hr = ID3D12Device3_CreateCommandAllocator(
 | 
			
		||||
    device,
 | 
			
		||||
    type,
 | 
			
		||||
    &IID_ID3D12CommandAllocator,
 | 
			
		||||
    (void **)allocator);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_ERROR("Failed to create the ID3D12CommandAllocator");
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
  ID3D12CommandAllocator_SetName(*allocator, name);
 | 
			
		||||
 | 
			
		||||
  comRef_defineLocal(ID3D12GraphicsCommandList, gfxList);
 | 
			
		||||
  hr = ID3D12Device3_CreateCommandList(
 | 
			
		||||
    device,
 | 
			
		||||
    0,
 | 
			
		||||
    type,
 | 
			
		||||
    *allocator,
 | 
			
		||||
    NULL,
 | 
			
		||||
    &IID_ID3D12GraphicsCommandList,
 | 
			
		||||
    (void **)gfxList);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to create ID3D12GraphicsCommandList", hr);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
  ID3D12GraphicsCommandList_SetName(*gfxList, name);
 | 
			
		||||
 | 
			
		||||
  comRef_defineLocal(ID3D12CommandList, cmdList);
 | 
			
		||||
  hr = ID3D12GraphicsCommandList_QueryInterface(
 | 
			
		||||
    *gfxList, &IID_ID3D12CommandList, (void **)cmdList);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to query the ID3D12CommandList interface", hr);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  comRef_defineLocal(ID3D12Fence, fence);
 | 
			
		||||
  hr = ID3D12Device3_CreateFence(
 | 
			
		||||
    device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)fence);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to create ID3D12Fence", hr);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Create the completion event for the fence
 | 
			
		||||
  HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL);
 | 
			
		||||
  if (!event)
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to create the completion event", GetLastError());
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  comRef_toGlobal(dst->allocator, allocator);
 | 
			
		||||
  comRef_toGlobal(dst->gfxList  , gfxList  );
 | 
			
		||||
  comRef_toGlobal(dst->cmdList  , cmdList  );
 | 
			
		||||
  comRef_toGlobal(dst->fence    , fence    );
 | 
			
		||||
  dst->event      = event;
 | 
			
		||||
  dst->fenceValue = 0;
 | 
			
		||||
 | 
			
		||||
  result = true;
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
  comRef_scopePop();
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void d12_freeCommandGroup(
 | 
			
		||||
  D12CommandGroup * grp)
 | 
			
		||||
{
 | 
			
		||||
  // com objet release is handled by comRef, but the handle is not
 | 
			
		||||
  if (grp->event)
 | 
			
		||||
  {
 | 
			
		||||
    CloseHandle(grp->event);
 | 
			
		||||
    grp->event = NULL;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool d12_executeCommandGroup(
 | 
			
		||||
  D12CommandGroup * grp)
 | 
			
		||||
{
 | 
			
		||||
  HRESULT hr = ID3D12GraphicsCommandList_Close(*grp->gfxList);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to close the command list", hr);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ID3D12CommandQueue_ExecuteCommandLists(
 | 
			
		||||
    *this->commandQueue, 1, grp->cmdList);
 | 
			
		||||
 | 
			
		||||
  hr = ID3D12CommandQueue_Signal(
 | 
			
		||||
    *this->commandQueue, *grp->fence, ++grp->fenceValue);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to set the fence signal", hr);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (ID3D12Fence_GetCompletedValue(*grp->fence) < grp->fenceValue)
 | 
			
		||||
  {
 | 
			
		||||
    ID3D12Fence_SetEventOnCompletion(*grp->fence, grp->fenceValue, grp->event);
 | 
			
		||||
    WaitForSingleObject(grp->event, INFINITE);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  hr = ID3D12CommandAllocator_Reset(*grp->allocator);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to reset the command allocator", hr);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  hr = ID3D12GraphicsCommandList_Reset(*grp->gfxList, *grp->allocator, NULL);
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to reset the graphics command list", hr);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ID3D12Resource * d12_frameBufferToResource(unsigned frameBufferIndex,
 | 
			
		||||
  FrameBuffer * frameBuffer, unsigned size)
 | 
			
		||||
{
 | 
			
		||||
  ID3D12Resource * result = NULL;
 | 
			
		||||
  comRef_scopePush(10);
 | 
			
		||||
 | 
			
		||||
  typeof(this->frameBuffers[0]) * fb = &this->frameBuffers[frameBufferIndex];
 | 
			
		||||
 | 
			
		||||
  // nothing to do if the resource is already setup and is big enough
 | 
			
		||||
  if (fb->resource && fb->frameBuffer == frameBuffer && fb->size >= size)
 | 
			
		||||
  {
 | 
			
		||||
    result = *fb->resource;
 | 
			
		||||
    ID3D12Resource_AddRef(result);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fb->size        = size;
 | 
			
		||||
  fb->frameBuffer = frameBuffer;
 | 
			
		||||
 | 
			
		||||
  D3D12_RESOURCE_DESC desc =
 | 
			
		||||
  {
 | 
			
		||||
    .Dimension          = D3D12_RESOURCE_DIMENSION_BUFFER,
 | 
			
		||||
    .Alignment          = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
 | 
			
		||||
    .Width              = size,
 | 
			
		||||
    .Height             = 1,
 | 
			
		||||
    .DepthOrArraySize   = 1,
 | 
			
		||||
    .MipLevels          = 1,
 | 
			
		||||
    .Format             = DXGI_FORMAT_UNKNOWN,
 | 
			
		||||
    .SampleDesc.Count   = 1,
 | 
			
		||||
    .SampleDesc.Quality = 0,
 | 
			
		||||
    .Layout             = D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
 | 
			
		||||
    .Flags              = D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  comRef_defineLocal(ID3D12Resource, resource);
 | 
			
		||||
  HRESULT hr = ID3D12Device3_CreatePlacedResource(
 | 
			
		||||
    *this->device,
 | 
			
		||||
    *this->ivshmemHeap,
 | 
			
		||||
    (uintptr_t)framebuffer_get_data(frameBuffer) - (uintptr_t)this->ivshmemBase,
 | 
			
		||||
    &desc,
 | 
			
		||||
    D3D12_RESOURCE_STATE_COPY_DEST,
 | 
			
		||||
    NULL,
 | 
			
		||||
    &IID_ID3D12Resource,
 | 
			
		||||
    (void **)resource);
 | 
			
		||||
 | 
			
		||||
  if (FAILED(hr))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WINERROR("Failed to create the FrameBuffer ID3D12Resource", hr);
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // cache the resource
 | 
			
		||||
  comRef_toGlobal(fb->resource, resource);
 | 
			
		||||
  result = *fb->resource;
 | 
			
		||||
  ID3D12Resource_AddRef(result);
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
  comRef_scopePop();
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void d12_updatePointer(CapturePointer * pointer, void * shape, size_t shapeSize)
 | 
			
		||||
{
 | 
			
		||||
  if (pointer->shapeUpdate)
 | 
			
		||||
  {
 | 
			
		||||
    void * dst;
 | 
			
		||||
    UINT   dstSize;
 | 
			
		||||
    if (!this->getPointerBufferFn(&dst, &dstSize))
 | 
			
		||||
    {
 | 
			
		||||
      DEBUG_ERROR("Failed to obtain a buffer for the pointer shape");
 | 
			
		||||
      pointer->shapeUpdate = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t copySize = min(dstSize, shapeSize);
 | 
			
		||||
    memcpy(dst, shape, copySize);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->postPointerBufferFn(pointer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct CaptureInterface Capture_D12 =
 | 
			
		||||
{
 | 
			
		||||
  .shortName       = "D12",
 | 
			
		||||
  .asyncCapture    = false,
 | 
			
		||||
  .getName         = d12_getName,
 | 
			
		||||
  .initOptions     = d12_initOptions,
 | 
			
		||||
  .create          = d12_create,
 | 
			
		||||
  .init            = d12_init,
 | 
			
		||||
  .stop            = d12_stop,
 | 
			
		||||
  .deinit          = d12_deinit,
 | 
			
		||||
  .free            = d12_free,
 | 
			
		||||
  .capture         = d12_capture,
 | 
			
		||||
  .waitFrame       = d12_waitFrame,
 | 
			
		||||
  .getFrame        = d12_getFrame
 | 
			
		||||
};
 | 
			
		||||
@@ -7,15 +7,12 @@ add_library(capture_DXGI STATIC
 | 
			
		||||
  src/d3d12.c
 | 
			
		||||
  src/ods_capture.c
 | 
			
		||||
  src/util.c
 | 
			
		||||
  src/com_ref.c
 | 
			
		||||
 | 
			
		||||
  src/pp/downsample.c
 | 
			
		||||
  src/pp/sdrwhitelevel.c
 | 
			
		||||
  src/pp/rgb24.c
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_definitions("-DCOBJMACROS -DINITGUID -DWIDL_C_INLINE_WRAPPERS")
 | 
			
		||||
 | 
			
		||||
target_link_libraries(capture_DXGI
 | 
			
		||||
  lg_common
 | 
			
		||||
  d3d11
 | 
			
		||||
 
 | 
			
		||||
@@ -1,153 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Looking Glass
 | 
			
		||||
 * Copyright © 2017-2023 The Looking Glass Authors
 | 
			
		||||
 * https://looking-glass.io
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms of the GNU General Public License as published by the Free
 | 
			
		||||
 * Software Foundation; either version 2 of the License, or (at your option)
 | 
			
		||||
 * any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 | 
			
		||||
 * more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License along
 | 
			
		||||
 * with this program; if not, write to the Free Software Foundation, Inc., 59
 | 
			
		||||
 * Temple Place, Suite 330, Boston, MA 02111-1307 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define COMREF_INTERNAL
 | 
			
		||||
#include "com_ref.h"
 | 
			
		||||
 | 
			
		||||
#include "common/debug.h"
 | 
			
		||||
#include "common/vector.h"
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  int          scope;
 | 
			
		||||
  IUnknown *   value;
 | 
			
		||||
  IUnknown *** ref;
 | 
			
		||||
}
 | 
			
		||||
COMRef;
 | 
			
		||||
 | 
			
		||||
static bool   comInit          = false;
 | 
			
		||||
static int    comScope         = -1;
 | 
			
		||||
static Vector comObjectsLocal  = {0};
 | 
			
		||||
static Vector comObjectsGlobal = {0};
 | 
			
		||||
 | 
			
		||||
bool comRef_init(unsigned globals, unsigned locals)
 | 
			
		||||
{
 | 
			
		||||
  if (comInit)
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
  if (!vector_create(&comObjectsGlobal, sizeof(COMRef), globals))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  if (!vector_create(&comObjectsLocal, sizeof(COMRef), locals))
 | 
			
		||||
  {
 | 
			
		||||
    vector_destroy(&comObjectsGlobal);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  comInit = true;
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void comRef_free(void)
 | 
			
		||||
{
 | 
			
		||||
  if (!comInit)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  COMRef * ref;
 | 
			
		||||
 | 
			
		||||
  if (comScope > -1)
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_WARN("There is %d unmatched `comRef_scopePush` calls", comScope+1);
 | 
			
		||||
    vector_forEachRef(ref, &comObjectsLocal)
 | 
			
		||||
      if (ref->value)
 | 
			
		||||
        IUnknown_Release(ref->value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  vector_forEachRef(ref, &comObjectsGlobal)
 | 
			
		||||
  {
 | 
			
		||||
    if (ref->ref)
 | 
			
		||||
      *ref->ref = NULL;
 | 
			
		||||
 | 
			
		||||
    if (ref->value)
 | 
			
		||||
      IUnknown_Release(ref->value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  comScope = -1;
 | 
			
		||||
  vector_destroy(&comObjectsLocal);
 | 
			
		||||
  vector_destroy(&comObjectsGlobal);
 | 
			
		||||
  comInit = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static IUnknown ** comRef_new(Vector * vector, IUnknown *** dst)
 | 
			
		||||
{
 | 
			
		||||
  DEBUG_ASSERT(comInit && "comRef has not been initialized");
 | 
			
		||||
 | 
			
		||||
  // we must not allow the vector to grow as if the realloc moves to a new
 | 
			
		||||
  // address it will invalidate any external pointers to members in it
 | 
			
		||||
  DEBUG_ASSERT(vector_size(vector) < vector_capacity(vector) &&
 | 
			
		||||
    "comRef vector too small!");
 | 
			
		||||
 | 
			
		||||
  COMRef * ref = (COMRef *)vector_push(vector, NULL);
 | 
			
		||||
  if (!ref)
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_ERROR("Failed to allocate ram for com object");
 | 
			
		||||
    return NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ref->scope = comScope;
 | 
			
		||||
  ref->ref   = dst;
 | 
			
		||||
  ref->value = NULL;
 | 
			
		||||
 | 
			
		||||
  if (dst)
 | 
			
		||||
    *dst = &ref->value;
 | 
			
		||||
 | 
			
		||||
  return &ref->value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IUnknown ** comRef_newGlobal(IUnknown *** dst)
 | 
			
		||||
{
 | 
			
		||||
  return comRef_new(&comObjectsGlobal, dst);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IUnknown ** comRef_newLocal(IUnknown *** dst)
 | 
			
		||||
{
 | 
			
		||||
  IUnknown ** ret = comRef_new(&comObjectsLocal, NULL);
 | 
			
		||||
  *dst = ret;
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void comRef_scopePush(void)
 | 
			
		||||
{
 | 
			
		||||
  DEBUG_ASSERT(comInit && "comRef has not been initialized");
 | 
			
		||||
  ++comScope;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void comRef_scopePop(void)
 | 
			
		||||
{
 | 
			
		||||
  DEBUG_ASSERT(comInit && "comRef has not been initialized");
 | 
			
		||||
  DEBUG_ASSERT(comScope >= 0);
 | 
			
		||||
 | 
			
		||||
  COMRef * ref;
 | 
			
		||||
  while(vector_size(&comObjectsLocal) > 0)
 | 
			
		||||
  {
 | 
			
		||||
    ref = (COMRef *)vector_ptrTo(&comObjectsLocal,
 | 
			
		||||
      vector_size(&comObjectsLocal) - 1);
 | 
			
		||||
 | 
			
		||||
    if (ref->scope < comScope)
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    if (ref->value)
 | 
			
		||||
      IUnknown_Release(ref->value);
 | 
			
		||||
 | 
			
		||||
    vector_pop(&comObjectsLocal);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  --comScope;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,99 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Looking Glass
 | 
			
		||||
 * Copyright © 2017-2023 The Looking Glass Authors
 | 
			
		||||
 * https://looking-glass.io
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms of the GNU General Public License as published by the Free
 | 
			
		||||
 * Software Foundation; either version 2 of the License, or (at your option)
 | 
			
		||||
 * any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 | 
			
		||||
 * more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License along
 | 
			
		||||
 * with this program; if not, write to the Free Software Foundation, Inc., 59
 | 
			
		||||
 * Temple Place, Suite 330, Boston, MA 02111-1307 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * These functions are to assist in tracking and relasing COM objects
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Initialize the com object tracking
 | 
			
		||||
 */
 | 
			
		||||
bool comRef_init(unsigned globals, unsigned locals);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Release globals and deinitialize the com object tracking
 | 
			
		||||
 */
 | 
			
		||||
void comRef_free(void);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create a new global COM reference
 | 
			
		||||
 */
 | 
			
		||||
IUnknown ** comRef_newGlobal(IUnknown *** dst);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create a new locally scoped COM reference
 | 
			
		||||
 */
 | 
			
		||||
IUnknown ** comRef_newLocal(IUnknown *** dst);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Define and create a new locally scoped COM reference
 | 
			
		||||
 */
 | 
			
		||||
#define comRef_defineLocal(type, name) \
 | 
			
		||||
  type ** name; \
 | 
			
		||||
  comRef_newLocal(&name);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Release a COM reference immediately
 | 
			
		||||
 * This is just a helper, the ref is still tracked if used again
 | 
			
		||||
 */
 | 
			
		||||
inline static ULONG comRef_release(IUnknown ** ref)
 | 
			
		||||
{
 | 
			
		||||
  if (!ref)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  ULONG count = 0;
 | 
			
		||||
  if (*ref)
 | 
			
		||||
    count = IUnknown_Release(*ref);
 | 
			
		||||
  *ref = NULL;
 | 
			
		||||
  return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create a new local scope
 | 
			
		||||
 */
 | 
			
		||||
void comRef_scopePush(void);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Exit from a local scope and release all locals
 | 
			
		||||
 */
 | 
			
		||||
void comRef_scopePop (void);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Macros to prevent needing to typecast calls to these methods
 | 
			
		||||
 */
 | 
			
		||||
#ifndef COMREF_INTERNAL
 | 
			
		||||
  #define comRef_newGlobal(dst) comRef_newGlobal((IUnknown ***)(dst))
 | 
			
		||||
  #define comRef_newLocal(dst)  comRef_newLocal((IUnknown ***)(dst))
 | 
			
		||||
  #define comRef_release(ref)   comRef_release((IUnknown **)(ref))
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Convert a local to a global
 | 
			
		||||
 */
 | 
			
		||||
#define comRef_toGlobal(dst, src) \
 | 
			
		||||
{ \
 | 
			
		||||
  IUnknown ** global = comRef_newGlobal(&(dst)); \
 | 
			
		||||
  DEBUG_ASSERT(global && "comRef_newGlobal failed\n"); \
 | 
			
		||||
  *global = (IUnknown*)*(src); \
 | 
			
		||||
  *(src)  = NULL; \
 | 
			
		||||
}
 | 
			
		||||
@@ -29,6 +29,8 @@
 | 
			
		||||
 | 
			
		||||
struct D3D11Backend
 | 
			
		||||
{
 | 
			
		||||
  ComScope * comScope;
 | 
			
		||||
 | 
			
		||||
  RunningAvg avgMapTime;
 | 
			
		||||
  uint64_t   usleepMapTime;
 | 
			
		||||
 | 
			
		||||
@@ -43,6 +45,9 @@ struct D3D11Backend
 | 
			
		||||
 | 
			
		||||
static struct D3D11Backend  * this = NULL;
 | 
			
		||||
 | 
			
		||||
#define comRef_toGlobal(dst, src) \
 | 
			
		||||
  _comRef_toGlobal(this->comScope, dst, src)
 | 
			
		||||
 | 
			
		||||
static bool d3d11_create(
 | 
			
		||||
  void     * ivshmemBase,
 | 
			
		||||
  unsigned * alignSize,
 | 
			
		||||
@@ -62,6 +67,8 @@ static bool d3d11_create(
 | 
			
		||||
 | 
			
		||||
  this->avgMapTime = runningavg_new(10);
 | 
			
		||||
  this->textures   = textures;
 | 
			
		||||
 | 
			
		||||
  comRef_initGlobalScope(10, this->comScope);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -72,6 +79,7 @@ static bool d3d11_configure(
 | 
			
		||||
  unsigned    bpp,
 | 
			
		||||
  unsigned *  pitch)
 | 
			
		||||
{
 | 
			
		||||
  comRef_scopePush(10);
 | 
			
		||||
  HRESULT status;
 | 
			
		||||
 | 
			
		||||
  D3D11_TEXTURE2D_DESC texTexDesc =
 | 
			
		||||
@@ -116,9 +124,11 @@ static bool d3d11_configure(
 | 
			
		||||
    *(ID3D11Resource **)this->texture[0].tex, 0);
 | 
			
		||||
 | 
			
		||||
  *pitch = mapping.RowPitch;
 | 
			
		||||
  comRef_scopePop();
 | 
			
		||||
  return true;
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
  comRef_scopePop();
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -128,6 +138,7 @@ static void d3d11_free(void)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  runningavg_free(&this->avgMapTime);
 | 
			
		||||
  comRef_freeScope(&this->comScope);
 | 
			
		||||
  free(this);
 | 
			
		||||
  this = NULL;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -51,6 +51,8 @@ struct SharedCache
 | 
			
		||||
 | 
			
		||||
struct D3D12Backend
 | 
			
		||||
{
 | 
			
		||||
  ComScope * comScope;
 | 
			
		||||
 | 
			
		||||
  HMODULE d3d12;
 | 
			
		||||
  unsigned width, height, pitch;
 | 
			
		||||
  DXGI_FORMAT format;
 | 
			
		||||
@@ -78,6 +80,9 @@ struct D3D12Backend
 | 
			
		||||
 | 
			
		||||
static struct D3D12Backend * this = NULL;
 | 
			
		||||
 | 
			
		||||
#define comRef_toGlobal(dst, src) \
 | 
			
		||||
  _comRef_toGlobal(this->comScope, dst, src)
 | 
			
		||||
 | 
			
		||||
typedef HRESULT (*D3D12CreateDevice_t)(
 | 
			
		||||
  IUnknown          *pAdapter,
 | 
			
		||||
  D3D_FEATURE_LEVEL MinimumFeatureLevel,
 | 
			
		||||
@@ -99,7 +104,6 @@ static bool d3d12_create(
 | 
			
		||||
  unsigned   textures)
 | 
			
		||||
{
 | 
			
		||||
  DEBUG_ASSERT(!this);
 | 
			
		||||
  comRef_scopePush();
 | 
			
		||||
  bool result = false;
 | 
			
		||||
  HRESULT status;
 | 
			
		||||
 | 
			
		||||
@@ -113,6 +117,9 @@ static bool d3d12_create(
 | 
			
		||||
    goto exit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  comRef_initGlobalScope(10, this->comScope);
 | 
			
		||||
  comRef_scopePush(10);
 | 
			
		||||
 | 
			
		||||
  this->d3d12 = LoadLibrary("d3d12.dll");
 | 
			
		||||
  if (!this->d3d12)
 | 
			
		||||
    goto exit;
 | 
			
		||||
@@ -252,7 +259,7 @@ static bool d3d12_configure(
 | 
			
		||||
{
 | 
			
		||||
  bool result = false;
 | 
			
		||||
  HRESULT status;
 | 
			
		||||
  comRef_scopePush();
 | 
			
		||||
  comRef_scopePush(10);
 | 
			
		||||
 | 
			
		||||
  this->width  = width;
 | 
			
		||||
  this->height = height;
 | 
			
		||||
@@ -350,6 +357,7 @@ static void d3d12_free(void)
 | 
			
		||||
  if (this->d3d12)
 | 
			
		||||
    FreeLibrary(this->d3d12);
 | 
			
		||||
 | 
			
		||||
  comRef_freeScope(&this->comScope);
 | 
			
		||||
  free(this);
 | 
			
		||||
  this = NULL;
 | 
			
		||||
}
 | 
			
		||||
@@ -360,7 +368,7 @@ static bool d3d12_preCopy(
 | 
			
		||||
  unsigned          frameBufferIndex,
 | 
			
		||||
  FrameBuffer     * frameBuffer)
 | 
			
		||||
{
 | 
			
		||||
  comRef_scopePush();
 | 
			
		||||
  comRef_scopePush(10);
 | 
			
		||||
  bool result = false;
 | 
			
		||||
 | 
			
		||||
  // we need to flush the DX11 context explicity or we get tons of lag
 | 
			
		||||
@@ -478,9 +486,7 @@ done:
 | 
			
		||||
    nsleep((uint64_t)(this->copySleep * 1000000));
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
  if (!result)
 | 
			
		||||
    comRef_scopePop();
 | 
			
		||||
 | 
			
		||||
  comRef_scopePop();
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -616,8 +622,6 @@ static bool d3d12_postCopy(ID3D11Texture2D * src, unsigned textureIndex)
 | 
			
		||||
  result = true;
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
  //push is in preCopy
 | 
			
		||||
  comRef_scopePop();
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -106,6 +106,8 @@ FrameDamage;
 | 
			
		||||
 | 
			
		||||
struct DXGIInterface
 | 
			
		||||
{
 | 
			
		||||
  ComScope * comScope;
 | 
			
		||||
 | 
			
		||||
  bool                       initialized;
 | 
			
		||||
  LARGE_INTEGER              perfFreq;
 | 
			
		||||
  LARGE_INTEGER              frameTime;
 | 
			
		||||
@@ -125,7 +127,6 @@ struct DXGIInterface
 | 
			
		||||
  D3D_FEATURE_LEVEL          featureLevel;
 | 
			
		||||
  IDXGIOutputDuplication  ** dup;
 | 
			
		||||
  int                        maxTextures;
 | 
			
		||||
  void                     * ivshmemBase;
 | 
			
		||||
  Texture                  * texture;
 | 
			
		||||
  int                        texRIndex;
 | 
			
		||||
  int                        texWIndex;
 | 
			
		||||
@@ -140,6 +141,7 @@ struct DXGIInterface
 | 
			
		||||
 | 
			
		||||
  CaptureGetPointerBuffer    getPointerBufferFn;
 | 
			
		||||
  CapturePostPointerBuffer   postPointerBufferFn;
 | 
			
		||||
  unsigned                   frameBuffers;
 | 
			
		||||
  LGEvent                  * frameEvent;
 | 
			
		||||
 | 
			
		||||
  unsigned int    formatVer;
 | 
			
		||||
@@ -160,6 +162,7 @@ struct DXGIInterface
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// locals
 | 
			
		||||
 | 
			
		||||
static struct DXGIInterface * this = NULL;
 | 
			
		||||
 | 
			
		||||
extern struct DXGICopyBackend copyBackendD3D11;
 | 
			
		||||
@@ -169,6 +172,11 @@ static struct DXGICopyBackend * backends[] = {
 | 
			
		||||
  ©BackendD3D11,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// defines
 | 
			
		||||
 | 
			
		||||
#define comRef_toGlobal(dst, src) \
 | 
			
		||||
  _comRef_toGlobal(this->comScope, dst, src)
 | 
			
		||||
 | 
			
		||||
// forwards
 | 
			
		||||
 | 
			
		||||
static bool          dxgi_deinit(void);
 | 
			
		||||
@@ -277,9 +285,9 @@ static void dxgi_initOptions(void)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool dxgi_create(
 | 
			
		||||
  void                   * ivshmemBase,
 | 
			
		||||
  CaptureGetPointerBuffer  getPointerBufferFn,
 | 
			
		||||
  CapturePostPointerBuffer postPointerBufferFn)
 | 
			
		||||
  CapturePostPointerBuffer postPointerBufferFn,
 | 
			
		||||
  unsigned                 frameBuffers)
 | 
			
		||||
{
 | 
			
		||||
  DEBUG_ASSERT(!this);
 | 
			
		||||
  this = calloc(1, sizeof(*this));
 | 
			
		||||
@@ -306,16 +314,18 @@ static bool dxgi_create(
 | 
			
		||||
  this->allowRGB24          = option_get_bool("dxgi", "allowRGB24");
 | 
			
		||||
  this->dwmFlush            = option_get_bool("dxgi", "dwmFlush");
 | 
			
		||||
  this->disableDamage       = option_get_bool("dxgi", "disableDamage");
 | 
			
		||||
  this->ivshmemBase         = ivshmemBase;
 | 
			
		||||
  this->texture             = calloc(this->maxTextures, sizeof(*this->texture));
 | 
			
		||||
  this->getPointerBufferFn  = getPointerBufferFn;
 | 
			
		||||
  this->postPointerBufferFn = postPointerBufferFn;
 | 
			
		||||
  this->frameBuffers        = frameBuffers;
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool initVertexShader(void)
 | 
			
		||||
{
 | 
			
		||||
  comRef_scopePush(10);
 | 
			
		||||
 | 
			
		||||
  static const char * vshaderSrc =
 | 
			
		||||
    "void main(\n"
 | 
			
		||||
    "  in  uint   vertexID : SV_VERTEXID,\n"
 | 
			
		||||
@@ -362,23 +372,16 @@ static bool initVertexShader(void)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  comRef_toGlobal(this->vshader, vshader);
 | 
			
		||||
  comRef_scopePop();
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool dxgi_init(unsigned * alignSize)
 | 
			
		||||
static bool dxgi_init(void * ivshmemBase, unsigned * alignSize)
 | 
			
		||||
{
 | 
			
		||||
  DEBUG_ASSERT(this);
 | 
			
		||||
 | 
			
		||||
  if (!comRef_init(
 | 
			
		||||
    20 + this->maxTextures * 16, //max total globals
 | 
			
		||||
    20                           //max total locals
 | 
			
		||||
  ))
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_ERROR("failed to intialize the comRef tracking");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  comRef_scopePush();
 | 
			
		||||
  comRef_initGlobalScope(20 + this->maxTextures * 16, this->comScope);
 | 
			
		||||
  comRef_scopePush(20);
 | 
			
		||||
 | 
			
		||||
  this->desktop = OpenInputDesktop(0, FALSE, GENERIC_READ);
 | 
			
		||||
  if (!this->desktop)
 | 
			
		||||
@@ -411,9 +414,10 @@ static bool dxgi_init(unsigned * alignSize)
 | 
			
		||||
 | 
			
		||||
  lgResetEvent(this->frameEvent);
 | 
			
		||||
 | 
			
		||||
  comRef_defineLocal(IDXGIFactory1, factory);
 | 
			
		||||
  status = CreateDXGIFactory2(this->debug ? DXGI_CREATE_FACTORY_DEBUG : 0,
 | 
			
		||||
    &IID_IDXGIFactory1,
 | 
			
		||||
    (void **)comRef_newGlobal(&this->factory));
 | 
			
		||||
    (void **)factory);
 | 
			
		||||
 | 
			
		||||
  if (FAILED(status))
 | 
			
		||||
  {
 | 
			
		||||
@@ -429,7 +433,7 @@ static bool dxgi_init(unsigned * alignSize)
 | 
			
		||||
 | 
			
		||||
  for (
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    IDXGIFactory1_EnumAdapters1(*this->factory, i, adapter)
 | 
			
		||||
    IDXGIFactory1_EnumAdapters1(*factory, i, adapter)
 | 
			
		||||
      != DXGI_ERROR_NOT_FOUND;
 | 
			
		||||
    ++i, comRef_release(adapter))
 | 
			
		||||
  {
 | 
			
		||||
@@ -509,6 +513,7 @@ static bool dxgi_init(unsigned * alignSize)
 | 
			
		||||
    goto fail;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  comRef_toGlobal(this->factory, factory);
 | 
			
		||||
  comRef_toGlobal(this->adapter, adapter);
 | 
			
		||||
  comRef_toGlobal(this->output , output );
 | 
			
		||||
 | 
			
		||||
@@ -569,6 +574,8 @@ static bool dxgi_init(unsigned * alignSize)
 | 
			
		||||
    goto fail;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  comRef_defineLocal(ID3D11Device       , device       );
 | 
			
		||||
  comRef_defineLocal(ID3D11DeviceContext, deviceContext);
 | 
			
		||||
  status = D3D11CreateDevice(
 | 
			
		||||
    *tmp,
 | 
			
		||||
    D3D_DRIVER_TYPE_UNKNOWN,
 | 
			
		||||
@@ -577,11 +584,9 @@ static bool dxgi_init(unsigned * alignSize)
 | 
			
		||||
      (this->debug ? D3D11_CREATE_DEVICE_DEBUG : 0),
 | 
			
		||||
    featureLevels, featureLevelCount,
 | 
			
		||||
    D3D11_SDK_VERSION,
 | 
			
		||||
    (ID3D11Device **)comRef_newGlobal(&this->device),
 | 
			
		||||
    device,
 | 
			
		||||
    &this->featureLevel,
 | 
			
		||||
    (ID3D11DeviceContext **)comRef_newGlobal(&this->deviceContext));
 | 
			
		||||
 | 
			
		||||
  LG_LOCK_INIT(this->deviceContextLock);
 | 
			
		||||
    deviceContext);
 | 
			
		||||
 | 
			
		||||
  if (FAILED(status))
 | 
			
		||||
  {
 | 
			
		||||
@@ -589,6 +594,10 @@ static bool dxgi_init(unsigned * alignSize)
 | 
			
		||||
    goto fail;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  comRef_toGlobal(this->device       , device       );
 | 
			
		||||
  comRef_toGlobal(this->deviceContext, deviceContext);
 | 
			
		||||
  LG_LOCK_INIT(this->deviceContextLock);
 | 
			
		||||
 | 
			
		||||
  switch(outputDesc.Rotation)
 | 
			
		||||
  {
 | 
			
		||||
    case DXGI_MODE_ROTATION_ROTATE90:
 | 
			
		||||
@@ -648,6 +657,8 @@ static bool dxgi_init(unsigned * alignSize)
 | 
			
		||||
    IDXGIDevice1_SetMaximumFrameLatency(*dxgi, 1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  comRef_defineLocal(IDXGIOutputDuplication, dup);
 | 
			
		||||
 | 
			
		||||
  comRef_defineLocal(IDXGIOutput5, output5);
 | 
			
		||||
  status = IDXGIOutput_QueryInterface(
 | 
			
		||||
    *this->output, &IID_IDXGIOutput5, (void **)output5);
 | 
			
		||||
@@ -671,8 +682,7 @@ static bool dxgi_init(unsigned * alignSize)
 | 
			
		||||
    for (int i = 0; i < 2; ++i)
 | 
			
		||||
    {
 | 
			
		||||
      status = IDXGIOutput1_DuplicateOutput(
 | 
			
		||||
        *output1, (IUnknown *)*this->device,
 | 
			
		||||
        (IDXGIOutputDuplication **)comRef_newGlobal(&this->dup));
 | 
			
		||||
        *output1, *(IUnknown **)this->device, dup);
 | 
			
		||||
 | 
			
		||||
      if (SUCCEEDED(status))
 | 
			
		||||
        break;
 | 
			
		||||
@@ -703,7 +713,7 @@ static bool dxgi_init(unsigned * alignSize)
 | 
			
		||||
        0,
 | 
			
		||||
        ARRAY_LENGTH(supportedFormats),
 | 
			
		||||
        supportedFormats,
 | 
			
		||||
        (IDXGIOutputDuplication **)comRef_newGlobal(&this->dup));
 | 
			
		||||
        dup);
 | 
			
		||||
 | 
			
		||||
      if (SUCCEEDED(status))
 | 
			
		||||
        break;
 | 
			
		||||
@@ -721,6 +731,8 @@ static bool dxgi_init(unsigned * alignSize)
 | 
			
		||||
      goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    comRef_toGlobal(this->dup, dup);
 | 
			
		||||
 | 
			
		||||
    comRef_defineLocal(IDXGIOutput6, output6);
 | 
			
		||||
    status = IDXGIOutput_QueryInterface(
 | 
			
		||||
      *this->output, &IID_IDXGIOutput6, (void **)output6);
 | 
			
		||||
@@ -806,9 +818,9 @@ static bool dxgi_init(unsigned * alignSize)
 | 
			
		||||
    if (!strcasecmp(copyBackend, backends[i]->code))
 | 
			
		||||
    {
 | 
			
		||||
      if (!backends[i]->create(
 | 
			
		||||
        this->ivshmemBase,
 | 
			
		||||
        ivshmemBase,
 | 
			
		||||
        alignSize,
 | 
			
		||||
        LGMP_Q_FRAME_LEN,
 | 
			
		||||
        this->frameBuffers,
 | 
			
		||||
        this->maxTextures))
 | 
			
		||||
      {
 | 
			
		||||
        DEBUG_ERROR("Failed to initialize selected capture backend: %s", backends[i]->name);
 | 
			
		||||
@@ -914,7 +926,7 @@ static bool dxgi_deinit(void)
 | 
			
		||||
    dxgi_releaseFrame();
 | 
			
		||||
 | 
			
		||||
  // this MUST run before backend->free() & ppFreeAll.
 | 
			
		||||
  comRef_free();
 | 
			
		||||
  comRef_freeScope(&this->comScope);
 | 
			
		||||
 | 
			
		||||
  ppFreeAll();
 | 
			
		||||
  if (this->backend)
 | 
			
		||||
@@ -1066,7 +1078,7 @@ static CaptureResult dxgi_capture(unsigned frameBufferIndex,
 | 
			
		||||
{
 | 
			
		||||
  DEBUG_ASSERT(this);
 | 
			
		||||
  DEBUG_ASSERT(this->initialized);
 | 
			
		||||
  comRef_scopePush();
 | 
			
		||||
  comRef_scopePush(10);
 | 
			
		||||
 | 
			
		||||
  Texture                 * tex = NULL;
 | 
			
		||||
  CaptureResult             result;
 | 
			
		||||
@@ -1386,7 +1398,7 @@ static CaptureResult dxgi_capture(unsigned frameBufferIndex,
 | 
			
		||||
  if (postPointer)
 | 
			
		||||
  {
 | 
			
		||||
    pointer.visible = this->lastPointerVisible;
 | 
			
		||||
    this->postPointerBufferFn(pointer);
 | 
			
		||||
    this->postPointerBufferFn(&pointer);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  result = CAPTURE_RESULT_OK;
 | 
			
		||||
@@ -1395,7 +1407,8 @@ exit:
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static CaptureResult dxgi_waitFrame(CaptureFrame * frame, const size_t maxFrameSize)
 | 
			
		||||
static CaptureResult dxgi_waitFrame(unsigned frameBufferIndex,
 | 
			
		||||
  CaptureFrame * frame, const size_t maxFrameSize)
 | 
			
		||||
{
 | 
			
		||||
  DEBUG_ASSERT(this);
 | 
			
		||||
  DEBUG_ASSERT(this->initialized);
 | 
			
		||||
@@ -1444,17 +1457,18 @@ static CaptureResult dxgi_waitFrame(CaptureFrame * frame, const size_t maxFrameS
 | 
			
		||||
  return CAPTURE_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static CaptureResult dxgi_getFrame(FrameBuffer * frame, int frameIndex)
 | 
			
		||||
static CaptureResult dxgi_getFrame(unsigned frameBufferIndex,
 | 
			
		||||
  FrameBuffer * frame, const size_t maxFrameSize)
 | 
			
		||||
{
 | 
			
		||||
  DEBUG_ASSERT(this);
 | 
			
		||||
  DEBUG_ASSERT(this->initialized);
 | 
			
		||||
 | 
			
		||||
  Texture     * tex    = &this->texture[this->texRIndex];
 | 
			
		||||
  FrameDamage * damage = &this->frameDamage[frameIndex];
 | 
			
		||||
  FrameDamage * damage = &this->frameDamage[frameBufferIndex];
 | 
			
		||||
 | 
			
		||||
  if (this->backend->writeFrame)
 | 
			
		||||
  {
 | 
			
		||||
    CaptureResult result = this->backend->writeFrame(frameIndex, frame);
 | 
			
		||||
    CaptureResult result = this->backend->writeFrame(frameBufferIndex, frame);
 | 
			
		||||
    if (result != CAPTURE_RESULT_OK)
 | 
			
		||||
      return result;
 | 
			
		||||
  }
 | 
			
		||||
@@ -1500,7 +1514,7 @@ static CaptureResult dxgi_getFrame(FrameBuffer * frame, int frameIndex)
 | 
			
		||||
  for (int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
 | 
			
		||||
  {
 | 
			
		||||
    struct FrameDamage * damage = this->frameDamage + i;
 | 
			
		||||
    if (i == frameIndex)
 | 
			
		||||
    if (i == frameBufferIndex)
 | 
			
		||||
      damage->count = 0;
 | 
			
		||||
    else if (tex->damageRectsCount > 0 && damage->count >= 0 &&
 | 
			
		||||
             damage->count + tex->damageRectsCount <= KVMFR_MAX_DAMAGE_RECTS)
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,8 @@
 | 
			
		||||
 | 
			
		||||
typedef struct Downsample
 | 
			
		||||
{
 | 
			
		||||
  ComScope * comScope;
 | 
			
		||||
 | 
			
		||||
  ID3D11Device        ** device;
 | 
			
		||||
  ID3D11DeviceContext ** context;
 | 
			
		||||
  bool shareable;
 | 
			
		||||
@@ -43,6 +45,9 @@ typedef struct Downsample
 | 
			
		||||
Downsample;
 | 
			
		||||
static Downsample this = {0};
 | 
			
		||||
 | 
			
		||||
#define comRef_toGlobal(dst, src) \
 | 
			
		||||
  _comRef_toGlobal(this.comScope, dst, src)
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  ID3D11Texture2D        ** tex;
 | 
			
		||||
@@ -73,11 +78,14 @@ static bool downsample_setup(
 | 
			
		||||
  this.device    = device;
 | 
			
		||||
  this.context   = context;
 | 
			
		||||
  this.shareable = shareable;
 | 
			
		||||
 | 
			
		||||
  comRef_initGlobalScope(10, this.comScope);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void downsample_finish(void)
 | 
			
		||||
{
 | 
			
		||||
  comRef_freeScope(&this.comScope);
 | 
			
		||||
  memset(&this, 0, sizeof(this));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -96,7 +104,7 @@ static bool downsample_configure(void * opaque,
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
  HRESULT status;
 | 
			
		||||
  comRef_scopePush();
 | 
			
		||||
  comRef_scopePush(10);
 | 
			
		||||
 | 
			
		||||
  if (!this.pshader)
 | 
			
		||||
  {
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,8 @@
 | 
			
		||||
 | 
			
		||||
typedef struct RGB24
 | 
			
		||||
{
 | 
			
		||||
  ComScope * comScope;
 | 
			
		||||
 | 
			
		||||
  ID3D11Device        ** device;
 | 
			
		||||
  ID3D11DeviceContext ** context;
 | 
			
		||||
  bool shareable;
 | 
			
		||||
@@ -40,6 +42,9 @@ typedef struct RGB24
 | 
			
		||||
RGB24;
 | 
			
		||||
static RGB24 this = {0};
 | 
			
		||||
 | 
			
		||||
#define comRef_toGlobal(dst, src) \
 | 
			
		||||
  _comRef_toGlobal(this.comScope, dst, src)
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  ID3D11Texture2D        ** tex;
 | 
			
		||||
@@ -55,6 +60,7 @@ static bool rgb24_setup(
 | 
			
		||||
  bool                   shareable
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
  comRef_initGlobalScope(10, this.comScope);
 | 
			
		||||
  this.device    = device;
 | 
			
		||||
  this.context   = context;
 | 
			
		||||
  this.shareable = shareable;
 | 
			
		||||
@@ -63,6 +69,7 @@ static bool rgb24_setup(
 | 
			
		||||
 | 
			
		||||
static void rgb24_finish(void)
 | 
			
		||||
{
 | 
			
		||||
  comRef_freeScope(&this.comScope);
 | 
			
		||||
  memset(&this, 0, sizeof(this));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -74,7 +81,8 @@ static bool rgb24_configure(void * opaque,
 | 
			
		||||
  RGB24Inst * inst = (RGB24Inst *)opaque;
 | 
			
		||||
 | 
			
		||||
  HRESULT status;
 | 
			
		||||
  comRef_scopePush();
 | 
			
		||||
 | 
			
		||||
  comRef_scopePush(10);
 | 
			
		||||
 | 
			
		||||
  if (!this.pshader)
 | 
			
		||||
  {
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,8 @@
 | 
			
		||||
 | 
			
		||||
typedef struct SDRWhiteLevel
 | 
			
		||||
{
 | 
			
		||||
  ComScope * comScope;
 | 
			
		||||
 | 
			
		||||
  ID3D11Device        ** device;
 | 
			
		||||
  ID3D11DeviceContext ** context;
 | 
			
		||||
 | 
			
		||||
@@ -43,6 +45,9 @@ typedef struct SDRWhiteLevel
 | 
			
		||||
SDRWhiteLevel;
 | 
			
		||||
static SDRWhiteLevel this = {0};
 | 
			
		||||
 | 
			
		||||
#define comRef_toGlobal(dst, src) \
 | 
			
		||||
  _comRef_toGlobal(this.comScope, dst, src)
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  ID3D11Texture2D        ** tex;
 | 
			
		||||
@@ -66,13 +71,15 @@ static bool sdrWhiteLevel_setup(
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
  bool result = false;
 | 
			
		||||
  comRef_scopePush();
 | 
			
		||||
  HRESULT status;
 | 
			
		||||
 | 
			
		||||
  this.device    = device;
 | 
			
		||||
  this.context   = context;
 | 
			
		||||
  this.shareable = shareable;
 | 
			
		||||
 | 
			
		||||
  comRef_initGlobalScope(10, this.comScope);
 | 
			
		||||
  comRef_scopePush(10);
 | 
			
		||||
 | 
			
		||||
  comRef_defineLocal(IDXGIOutput6, output6);
 | 
			
		||||
  status = IDXGIOutput_QueryInterface(
 | 
			
		||||
    *output, &IID_IDXGIOutput6, (void **)output6);
 | 
			
		||||
@@ -139,9 +146,8 @@ static bool sdrWhiteLevel_setup(
 | 
			
		||||
    .MaxLOD         = D3D11_FLOAT32_MAX
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  status = ID3D11Device_CreateSamplerState(
 | 
			
		||||
    *this.device, &samplerDesc,
 | 
			
		||||
    (ID3D11SamplerState  **)comRef_newGlobal(&this.sampler));
 | 
			
		||||
  comRef_defineLocal(ID3D11SamplerState, sampler);
 | 
			
		||||
  status = ID3D11Device_CreateSamplerState(*this.device, &samplerDesc, sampler);
 | 
			
		||||
 | 
			
		||||
  if (FAILED(status))
 | 
			
		||||
  {
 | 
			
		||||
@@ -156,9 +162,10 @@ static bool sdrWhiteLevel_setup(
 | 
			
		||||
    .BindFlags      = D3D11_BIND_CONSTANT_BUFFER,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  comRef_defineLocal(ID3D11Buffer, buffer);
 | 
			
		||||
  status = ID3D11Device_CreateBuffer(
 | 
			
		||||
    *this.device, &bufferDesc, NULL,
 | 
			
		||||
    (ID3D11Buffer **)comRef_newGlobal(&this.buffer));
 | 
			
		||||
    buffer);
 | 
			
		||||
 | 
			
		||||
  if (FAILED(status))
 | 
			
		||||
  {
 | 
			
		||||
@@ -169,6 +176,8 @@ static bool sdrWhiteLevel_setup(
 | 
			
		||||
  updateConsts();
 | 
			
		||||
  DEBUG_INFO("SDR White Level   : %f"   , this.sdrWhiteLevel);
 | 
			
		||||
 | 
			
		||||
  comRef_toGlobal(this.sampler, sampler);
 | 
			
		||||
  comRef_toGlobal(this.buffer , buffer );
 | 
			
		||||
  result = true;
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
@@ -178,6 +187,7 @@ exit:
 | 
			
		||||
 | 
			
		||||
static void sdrWhiteLevel_finish(void)
 | 
			
		||||
{
 | 
			
		||||
  comRef_freeScope(&this.comScope);
 | 
			
		||||
  memset(&this, 0, sizeof(this));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -225,7 +235,7 @@ static bool sdrWhiteLevel_configure(void * opaque,
 | 
			
		||||
  if (inst->tex)
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
  comRef_scopePush();
 | 
			
		||||
  comRef_scopePush(10);
 | 
			
		||||
 | 
			
		||||
  // create the output texture
 | 
			
		||||
  D3D11_TEXTURE2D_DESC texDesc =
 | 
			
		||||
 
 | 
			
		||||
@@ -128,7 +128,7 @@ static void on_mouseMove(int x, int y)
 | 
			
		||||
    .y              = y - this->mouseHotY
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  this->postPointerBufferFn(pointer);
 | 
			
		||||
  this->postPointerBufferFn(&pointer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char * nvfbc_getName(void)
 | 
			
		||||
@@ -185,9 +185,9 @@ static void nvfbc_initOptions(void)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool nvfbc_create(
 | 
			
		||||
  void                   * ivshmemBase,
 | 
			
		||||
  CaptureGetPointerBuffer  getPointerBufferFn,
 | 
			
		||||
  CapturePostPointerBuffer postPointerBufferFn)
 | 
			
		||||
  CapturePostPointerBuffer postPointerBufferFn,
 | 
			
		||||
  unsigned                 frameBuffers)
 | 
			
		||||
{
 | 
			
		||||
  if (!NvFBCInit())
 | 
			
		||||
    return false;
 | 
			
		||||
@@ -221,7 +221,7 @@ static void updateScale(void)
 | 
			
		||||
  this->targetHeight = this->height;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool nvfbc_init(unsigned * alignSize)
 | 
			
		||||
static bool nvfbc_init(void * ivshmemBase, unsigned * alignSize)
 | 
			
		||||
{
 | 
			
		||||
  int adapterIndex = option_get_int("nvfbc", "adapterIndex");
 | 
			
		||||
 | 
			
		||||
@@ -650,8 +650,8 @@ done:
 | 
			
		||||
  frame->damageRectsCount = rectId;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static CaptureResult nvfbc_waitFrame(CaptureFrame * frame,
 | 
			
		||||
    const size_t maxFrameSize)
 | 
			
		||||
static CaptureResult nvfbc_waitFrame(unsigned frameBufferIndex,
 | 
			
		||||
    CaptureFrame * frame, const size_t maxFrameSize)
 | 
			
		||||
{
 | 
			
		||||
  if (unlikely(this->stop))
 | 
			
		||||
    return CAPTURE_RESULT_REINIT;
 | 
			
		||||
@@ -711,12 +711,13 @@ static CaptureResult nvfbc_waitFrame(CaptureFrame * frame,
 | 
			
		||||
  return CAPTURE_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static CaptureResult nvfbc_getFrame(FrameBuffer * frame, int frameIndex)
 | 
			
		||||
static CaptureResult nvfbc_getFrame(unsigned frameBufferIndex,
 | 
			
		||||
    FrameBuffer * frame, const size_t maxFrameSize)
 | 
			
		||||
{
 | 
			
		||||
  const unsigned int h = DIFF_MAP_DIM(this->grabHeight, this->diffShift);
 | 
			
		||||
  const unsigned int w = DIFF_MAP_DIM(this->grabWidth,  this->diffShift);
 | 
			
		||||
  uint8_t * frameData = framebuffer_get_data(frame);
 | 
			
		||||
  struct FrameInfo * info = this->frameInfo + frameIndex;
 | 
			
		||||
  struct FrameInfo * info = this->frameInfo + frameBufferIndex;
 | 
			
		||||
 | 
			
		||||
  if (info->width == this->grabWidth && info->height == this->grabHeight)
 | 
			
		||||
  {
 | 
			
		||||
@@ -791,7 +792,7 @@ static CaptureResult nvfbc_getFrame(FrameBuffer * frame, int frameIndex)
 | 
			
		||||
 | 
			
		||||
  for (int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
 | 
			
		||||
  {
 | 
			
		||||
    if (i == frameIndex)
 | 
			
		||||
    if (i == frameBufferIndex)
 | 
			
		||||
    {
 | 
			
		||||
      this->frameInfo[i].width    = this->grabWidth;
 | 
			
		||||
      this->frameInfo[i].height   = this->grabHeight;
 | 
			
		||||
@@ -855,7 +856,7 @@ static int pointerThread(void * unused)
 | 
			
		||||
    pointer.x = this->mouseX - pointer.hx;
 | 
			
		||||
    pointer.y = this->mouseY - pointer.hy;
 | 
			
		||||
 | 
			
		||||
    this->postPointerBufferFn(pointer);
 | 
			
		||||
    this->postPointerBufferFn(&pointer);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										98
									
								
								host/platform/Windows/include/com_ref.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								host/platform/Windows/include/com_ref.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Looking Glass
 | 
			
		||||
 * Copyright © 2017-2023 The Looking Glass Authors
 | 
			
		||||
 * https://looking-glass.io
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms of the GNU General Public License as published by the Free
 | 
			
		||||
 * Software Foundation; either version 2 of the License, or (at your option)
 | 
			
		||||
 * any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 | 
			
		||||
 * more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License along
 | 
			
		||||
 * with this program; if not, write to the Free Software Foundation, Inc., 59
 | 
			
		||||
 * Temple Place, Suite 330, Boston, MA 02111-1307 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#include <malloc.h>
 | 
			
		||||
 | 
			
		||||
#include "common/locking.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * These functions are to assist in tracking and releasing COM objects
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
typedef struct ComScope ComScope;
 | 
			
		||||
 | 
			
		||||
struct ComScope
 | 
			
		||||
{
 | 
			
		||||
  bool          threadSafe;
 | 
			
		||||
  LG_Lock       lock;
 | 
			
		||||
 | 
			
		||||
  unsigned      size;
 | 
			
		||||
  unsigned      used;
 | 
			
		||||
  struct
 | 
			
		||||
  {
 | 
			
		||||
    IUnknown *** ptr;
 | 
			
		||||
    IUnknown   * ref;
 | 
			
		||||
  }
 | 
			
		||||
  * refs;
 | 
			
		||||
 | 
			
		||||
  void (*free)(void * ptr);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void comRef_initScope(unsigned size, ComScope ** instance,
 | 
			
		||||
  void *(allocFn)(size_t size), void (freeFn)(void * ptr), bool threadSafe);
 | 
			
		||||
 | 
			
		||||
void comRef_freeScope(ComScope ** instance);
 | 
			
		||||
 | 
			
		||||
IUnknown ** comRef_new(ComScope * scope, IUnknown *** dst);
 | 
			
		||||
 | 
			
		||||
#define comRef_initGlobalScope(size, scope) \
 | 
			
		||||
  comRef_initScope((size), &(scope), malloc, free, true)
 | 
			
		||||
 | 
			
		||||
#define comRef_freeGlobalScope(scope) \
 | 
			
		||||
  comRef_freeScope(&(scope))
 | 
			
		||||
 | 
			
		||||
#define comRef_scopePush(size) \
 | 
			
		||||
  ComScope * _comRef_localScope = alloca(sizeof(*_comRef_localScope) + \
 | 
			
		||||
    sizeof(*(_comRef_localScope->refs)) * size); \
 | 
			
		||||
  comRef_initScope(size, &_comRef_localScope, NULL, NULL, false);
 | 
			
		||||
 | 
			
		||||
#define comRef_scopePop() \
 | 
			
		||||
  comRef_freeScope(&_comRef_localScope)
 | 
			
		||||
 | 
			
		||||
#define comRef_defineLocal(type, name) \
 | 
			
		||||
  type ** name = NULL; \
 | 
			
		||||
  comRef_new(_comRef_localScope, (IUnknown ***)&(name));
 | 
			
		||||
 | 
			
		||||
#define _comRef_toGlobal(globalScope, dst, src) \
 | 
			
		||||
{ \
 | 
			
		||||
  IUnknown ** global = comRef_new((globalScope), (IUnknown ***)&(dst)); \
 | 
			
		||||
  *global = (IUnknown *)*(src); \
 | 
			
		||||
  *(src)  = NULL; \
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Release a COM reference immediately
 | 
			
		||||
 * This is just a helper, the ref is still tracked if used again
 | 
			
		||||
 */
 | 
			
		||||
inline static ULONG comRef_release(IUnknown ** ref)
 | 
			
		||||
{
 | 
			
		||||
  if (!ref)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  ULONG count = 0;
 | 
			
		||||
  if (*ref)
 | 
			
		||||
    count = IUnknown_Release(*ref);
 | 
			
		||||
  *ref = NULL;
 | 
			
		||||
  return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define comRef_release(ref) comRef_release((IUnknown **)(ref))
 | 
			
		||||
							
								
								
									
										108
									
								
								host/platform/Windows/src/com_ref.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								host/platform/Windows/src/com_ref.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Looking Glass
 | 
			
		||||
 * Copyright © 2017-2023 The Looking Glass Authors
 | 
			
		||||
 * https://looking-glass.io
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms of the GNU General Public License as published by the Free
 | 
			
		||||
 * Software Foundation; either version 2 of the License, or (at your option)
 | 
			
		||||
 * any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 | 
			
		||||
 * more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License along
 | 
			
		||||
 * with this program; if not, write to the Free Software Foundation, Inc., 59
 | 
			
		||||
 * Temple Place, Suite 330, Boston, MA 02111-1307 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "com_ref.h"
 | 
			
		||||
 | 
			
		||||
#include "common/debug.h"
 | 
			
		||||
 | 
			
		||||
void comRef_initScope(unsigned size, ComScope ** instance,
 | 
			
		||||
  void *(allocFn)(size_t size), void (freeFn)(void * ptr), bool threadSafe)
 | 
			
		||||
{
 | 
			
		||||
  ComScope * scope = *instance;
 | 
			
		||||
 | 
			
		||||
  const size_t ttlSize = sizeof(*scope) + sizeof(*(scope->refs)) * size;
 | 
			
		||||
  if (allocFn)
 | 
			
		||||
    scope = allocFn(ttlSize);
 | 
			
		||||
  DEBUG_ASSERT(scope && "No memory for scope");
 | 
			
		||||
 | 
			
		||||
  memset(scope, 0, ttlSize);
 | 
			
		||||
  scope->threadSafe = threadSafe;
 | 
			
		||||
  if (threadSafe)
 | 
			
		||||
    LG_LOCK_INIT(scope->lock);
 | 
			
		||||
 | 
			
		||||
  scope->size       = size;
 | 
			
		||||
  scope->refs       = (typeof(scope->refs))(scope+1);
 | 
			
		||||
  scope->free       = freeFn;
 | 
			
		||||
 | 
			
		||||
  *instance = scope;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void comRef_freeScope(ComScope ** instance)
 | 
			
		||||
{
 | 
			
		||||
  if (!*instance)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  ComScope * scope = *instance;
 | 
			
		||||
  for(unsigned i = 0; i < scope->used; ++i)
 | 
			
		||||
  {
 | 
			
		||||
    typeof(scope->refs) ref = &scope->refs[i];
 | 
			
		||||
    if (ref->ref)
 | 
			
		||||
    {
 | 
			
		||||
      IUnknown_Release(ref->ref);
 | 
			
		||||
      ref->ref = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *ref->ptr = NULL;
 | 
			
		||||
    ref->ptr = NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (scope->threadSafe)
 | 
			
		||||
    LG_LOCK_FREE(scope->lock);
 | 
			
		||||
 | 
			
		||||
  if (scope->free)
 | 
			
		||||
    scope->free(scope);
 | 
			
		||||
 | 
			
		||||
  *instance = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IUnknown ** comRef_new(ComScope * scope, IUnknown *** dst)
 | 
			
		||||
{
 | 
			
		||||
  /* check if the value it points to is already in our memory range and if it
 | 
			
		||||
   * does, then reuse it */
 | 
			
		||||
  if ((uintptr_t)*dst >= (uintptr_t)(scope->refs) &&
 | 
			
		||||
      (uintptr_t)*dst <  (uintptr_t)(scope->refs + scope->used))
 | 
			
		||||
  {
 | 
			
		||||
    // if it already holds a value, release it before we overwrite
 | 
			
		||||
    if (**dst)
 | 
			
		||||
    {
 | 
			
		||||
      IUnknown_Release(**dst);
 | 
			
		||||
      **dst = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // return the existing member
 | 
			
		||||
    return *dst;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* If you hit this, you need to enlarge the scope size, or check if you have a
 | 
			
		||||
   * resource leak. */
 | 
			
		||||
  DEBUG_ASSERT(scope->used < scope->size && "ComRef Scope Full");
 | 
			
		||||
 | 
			
		||||
  if (scope->threadSafe)
 | 
			
		||||
    LG_LOCK(scope->lock);
 | 
			
		||||
 | 
			
		||||
  scope->refs[scope->used].ptr = dst;
 | 
			
		||||
  *dst = &scope->refs[scope->used].ref;
 | 
			
		||||
  ++scope->used;
 | 
			
		||||
 | 
			
		||||
  if (scope->threadSafe)
 | 
			
		||||
    LG_UNLOCK(scope->lock);
 | 
			
		||||
 | 
			
		||||
  return *dst;
 | 
			
		||||
}
 | 
			
		||||
@@ -78,6 +78,7 @@ struct app
 | 
			
		||||
  int exitcode;
 | 
			
		||||
 | 
			
		||||
  PLGMPHost lgmp;
 | 
			
		||||
  void *ivshmemBase;
 | 
			
		||||
 | 
			
		||||
  PLGMPHostQueue pointerQueue;
 | 
			
		||||
  PLGMPMemory    pointerMemory[LGMP_Q_POINTER_LEN];
 | 
			
		||||
@@ -96,7 +97,8 @@ struct app
 | 
			
		||||
  KVMFRFrame   * frame      [LGMP_Q_FRAME_LEN];
 | 
			
		||||
  FrameBuffer  * frameBuffer[LGMP_Q_FRAME_LEN];
 | 
			
		||||
 | 
			
		||||
  unsigned int   frameIndex;
 | 
			
		||||
  unsigned int   captureIndex;
 | 
			
		||||
  unsigned int   readIndex;
 | 
			
		||||
  bool           frameValid;
 | 
			
		||||
  uint32_t       frameSerial;
 | 
			
		||||
 | 
			
		||||
@@ -181,7 +183,7 @@ static bool lgmpTimer(void * opaque)
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool sendFrame(void)
 | 
			
		||||
static bool sendFrame(CaptureResult result)
 | 
			
		||||
{
 | 
			
		||||
  CaptureFrame frame = { 0 };
 | 
			
		||||
  bool repeatFrame = false;
 | 
			
		||||
@@ -197,7 +199,11 @@ static bool sendFrame(void)
 | 
			
		||||
  if (app.state != APP_STATE_RUNNING)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  switch(app.iface->waitFrame(&frame, app.maxFrameSize))
 | 
			
		||||
  // only wait if the result from the capture was OK
 | 
			
		||||
  if (result == CAPTURE_RESULT_OK)
 | 
			
		||||
    result = app.iface->waitFrame(app.captureIndex, &frame, app.maxFrameSize);
 | 
			
		||||
 | 
			
		||||
  switch(result)
 | 
			
		||||
  {
 | 
			
		||||
    case CAPTURE_RESULT_OK:
 | 
			
		||||
      // reading the new subs count zeros it
 | 
			
		||||
@@ -236,17 +242,12 @@ static bool sendFrame(void)
 | 
			
		||||
  if (repeatFrame)
 | 
			
		||||
  {
 | 
			
		||||
    if ((status = lgmpHostQueuePost(app.frameQueue, 0,
 | 
			
		||||
           app.frameMemory[app.frameIndex])) != LGMP_OK)
 | 
			
		||||
           app.frameMemory[app.readIndex])) != LGMP_OK)
 | 
			
		||||
      DEBUG_ERROR("%s", lgmpStatusString(status));
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // we increment the index first so that if we need to repeat a frame
 | 
			
		||||
  // the index still points to the latest valid frame
 | 
			
		||||
  if (++app.frameIndex == LGMP_Q_FRAME_LEN)
 | 
			
		||||
    app.frameIndex = 0;
 | 
			
		||||
 | 
			
		||||
  KVMFRFrame * fi = app.frame[app.frameIndex];
 | 
			
		||||
  KVMFRFrame * fi = app.frame[app.captureIndex];
 | 
			
		||||
  KVMFRFrameFlags flags =
 | 
			
		||||
    (frame.hdr   ? FRAME_FLAG_HDR    : 0) |
 | 
			
		||||
    (frame.hdrPQ ? FRAME_FLAG_HDR_PQ : 0);
 | 
			
		||||
@@ -322,17 +323,24 @@ static bool sendFrame(void)
 | 
			
		||||
 | 
			
		||||
  app.frameValid = true;
 | 
			
		||||
 | 
			
		||||
  framebuffer_prepare(app.frameBuffer[app.frameIndex]);
 | 
			
		||||
  framebuffer_prepare(app.frameBuffer[app.captureIndex]);
 | 
			
		||||
 | 
			
		||||
  /* we post and then get the frame, this is intentional! */
 | 
			
		||||
  if ((status = lgmpHostQueuePost(app.frameQueue, 0,
 | 
			
		||||
    app.frameMemory[app.frameIndex])) != LGMP_OK)
 | 
			
		||||
    app.frameMemory[app.captureIndex])) != LGMP_OK)
 | 
			
		||||
  {
 | 
			
		||||
    DEBUG_ERROR("%s", lgmpStatusString(status));
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  app.iface->getFrame(app.frameBuffer[app.frameIndex], app.frameIndex);
 | 
			
		||||
  app.iface->getFrame(
 | 
			
		||||
    app.captureIndex,
 | 
			
		||||
    app.frameBuffer[app.captureIndex],
 | 
			
		||||
    app.maxFrameSize);
 | 
			
		||||
 | 
			
		||||
  app.readIndex = app.captureIndex;
 | 
			
		||||
  if (++app.captureIndex == LGMP_Q_FRAME_LEN)
 | 
			
		||||
    app.captureIndex = 0;
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -342,7 +350,7 @@ static int frameThread(void * opaque)
 | 
			
		||||
 | 
			
		||||
  while(app.state == APP_STATE_RUNNING)
 | 
			
		||||
  {
 | 
			
		||||
    if (!sendFrame())
 | 
			
		||||
    if (!sendFrame(CAPTURE_RESULT_OK))
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  DEBUG_INFO("Frame thread stopped");
 | 
			
		||||
@@ -392,7 +400,7 @@ static bool captureStart(void)
 | 
			
		||||
{
 | 
			
		||||
  if (app.state == APP_STATE_IDLE)
 | 
			
		||||
  {
 | 
			
		||||
    if (!app.iface->init(&app.alignSize))
 | 
			
		||||
    if (!app.iface->init(app.ivshmemBase, &app.alignSize))
 | 
			
		||||
    {
 | 
			
		||||
      DEBUG_ERROR("Failed to initialize the capture device");
 | 
			
		||||
      return false;
 | 
			
		||||
@@ -419,6 +427,7 @@ static bool captureStop(void)
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  app.frameValid = false;
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -527,17 +536,17 @@ static void sendPointer(bool newClient)
 | 
			
		||||
  postPointer(flags, mem);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void capturePostPointerBuffer(CapturePointer pointer)
 | 
			
		||||
void capturePostPointerBuffer(const CapturePointer * pointer)
 | 
			
		||||
{
 | 
			
		||||
  LG_LOCK(app.pointerLock);
 | 
			
		||||
 | 
			
		||||
  int x = app.pointerInfo.x;
 | 
			
		||||
  int y = app.pointerInfo.y;
 | 
			
		||||
 | 
			
		||||
  memcpy(&app.pointerInfo, &pointer, sizeof(CapturePointer));
 | 
			
		||||
  memcpy(&app.pointerInfo, pointer, sizeof(CapturePointer));
 | 
			
		||||
 | 
			
		||||
  /* if there was not a position update, restore the x & y */
 | 
			
		||||
  if (!pointer.positionUpdate)
 | 
			
		||||
  if (!pointer->positionUpdate)
 | 
			
		||||
  {
 | 
			
		||||
    app.pointerInfo.x = x;
 | 
			
		||||
    app.pointerInfo.y = y;
 | 
			
		||||
@@ -829,6 +838,7 @@ int app_main(int argc, char * argv[])
 | 
			
		||||
    DEBUG_ERROR("Failed to open the IVSHMEM device");
 | 
			
		||||
    return LG_HOST_EXIT_FATAL;
 | 
			
		||||
  }
 | 
			
		||||
  app.ivshmemBase = shmDev.mem;
 | 
			
		||||
 | 
			
		||||
  int exitcode  = 0;
 | 
			
		||||
  DEBUG_INFO("IVSHMEM Size     : %u MiB", shmDev.size / 1048576);
 | 
			
		||||
@@ -856,15 +866,15 @@ int app_main(int argc, char * argv[])
 | 
			
		||||
      DEBUG_INFO("Trying           : %s", iface->getName());
 | 
			
		||||
 | 
			
		||||
      if (!iface->create(
 | 
			
		||||
        shmDev.mem,
 | 
			
		||||
        captureGetPointerBuffer,
 | 
			
		||||
        capturePostPointerBuffer))
 | 
			
		||||
        capturePostPointerBuffer,
 | 
			
		||||
        ARRAY_LENGTH(app.frameBuffer)))
 | 
			
		||||
      {
 | 
			
		||||
        iface = NULL;
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (iface->init(&app.alignSize))
 | 
			
		||||
      if (iface->init(app.ivshmemBase, &app.alignSize))
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      iface->free();
 | 
			
		||||
@@ -965,12 +975,8 @@ int app_main(int argc, char * argv[])
 | 
			
		||||
 | 
			
		||||
      const uint64_t captureStart = microtime();
 | 
			
		||||
 | 
			
		||||
      unsigned nextIndex = app.frameIndex + 1;
 | 
			
		||||
      if (nextIndex == LGMP_Q_FRAME_LEN)
 | 
			
		||||
        nextIndex = 0;
 | 
			
		||||
 | 
			
		||||
      const CaptureResult result = app.iface->capture(
 | 
			
		||||
        nextIndex, app.frameBuffer[nextIndex]);
 | 
			
		||||
        app.captureIndex, app.frameBuffer[app.captureIndex]);
 | 
			
		||||
 | 
			
		||||
      if (likely(result == CAPTURE_RESULT_OK))
 | 
			
		||||
        previousFrameTime = captureStart;
 | 
			
		||||
@@ -982,7 +988,7 @@ int app_main(int argc, char * argv[])
 | 
			
		||||
          {
 | 
			
		||||
            LGMP_STATUS status;
 | 
			
		||||
            if ((status = lgmpHostQueuePost(app.frameQueue, 0,
 | 
			
		||||
                    app.frameMemory[app.frameIndex])) != LGMP_OK)
 | 
			
		||||
                    app.frameMemory[app.readIndex])) != LGMP_OK)
 | 
			
		||||
              DEBUG_ERROR("%s", lgmpStatusString(status));
 | 
			
		||||
          }
 | 
			
		||||
      }
 | 
			
		||||
@@ -1005,7 +1011,7 @@ int app_main(int argc, char * argv[])
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!app.iface->asyncCapture)
 | 
			
		||||
        sendFrame();
 | 
			
		||||
        sendFrame(result);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (app.state != APP_STATE_SHUTDOWN)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user