Add and update samples for cuda 10.2 support

This commit is contained in:
Mahesh Doijade
2019-10-23 19:00:39 +05:30
parent 489d9f7b1f
commit 6be514679b
305 changed files with 78239 additions and 518 deletions

160
Common/dynlink_d3d11.h Normal file
View File

@@ -0,0 +1,160 @@
/* Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//--------------------------------------------------------------------------------------
// File: dynlink_d3d11.h
//
// Shortcut macros and functions for using DX objects
//
// Copyright (c) Microsoft Corporation. All rights reserved
//--------------------------------------------------------------------------------------
#ifndef _DYNLINK_D3D11_H_
#define _DYNLINK_D3D11_H_
// Standard Windows includes
#include <windows.h>
#include <initguid.h>
#include <assert.h>
#include <wchar.h>
#include <mmsystem.h>
#include <commctrl.h> // for InitCommonControls()
#include <shellapi.h> // for ExtractIcon()
#include <new.h> // for placement new
#include <shlobj.h>
#include <math.h>
#include <limits.h>
#include <stdio.h>
// CRT's memory leak detection
#if defined(DEBUG) || defined(_DEBUG)
#include <crtdbg.h>
#endif
// Direct3D10 includes
#include <dxgi.h>
#include <d3d11.h>
// #include <..\Samples\C++\Effects11\Inc\d3dx11effect.h>
// XInput includes
#include <xinput.h>
// strsafe.h deprecates old unsecure string functions. If you
// really do not want to it to (not recommended), then uncomment the next line
//#define STRSAFE_NO_DEPRECATE
#ifndef STRSAFE_NO_DEPRECATE
#pragma deprecated("strncpy")
#pragma deprecated("wcsncpy")
#pragma deprecated("_tcsncpy")
#pragma deprecated("wcsncat")
#pragma deprecated("strncat")
#pragma deprecated("_tcsncat")
#endif
#pragma warning( disable : 4996 ) // disable deprecated warning
#include <strsafe.h>
#pragma warning( default : 4996 )
typedef HRESULT(WINAPI *LPCREATEDXGIFACTORY)(REFIID, void **);
typedef HRESULT(WINAPI *LPD3D11CREATEDEVICEANDSWAPCHAIN)(__in_opt IDXGIAdapter *pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags, __in_ecount_opt(FeatureLevels) CONST D3D_FEATURE_LEVEL *pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, __in_opt CONST DXGI_SWAP_CHAIN_DESC *pSwapChainDesc, __out_opt IDXGISwapChain **ppSwapChain, __out_opt ID3D11Device **ppDevice, __out_opt D3D_FEATURE_LEVEL *pFeatureLevel, __out_opt ID3D11DeviceContext **ppImmediateContext);
typedef HRESULT(WINAPI *LPD3D11CREATEDEVICE)(IDXGIAdapter *, D3D_DRIVER_TYPE, HMODULE, UINT32, D3D_FEATURE_LEVEL *, UINT, UINT32, ID3D11Device **, D3D_FEATURE_LEVEL *, ID3D11DeviceContext **);
static HMODULE s_hModDXGI = NULL;
static LPCREATEDXGIFACTORY sFnPtr_CreateDXGIFactory = NULL;
static HMODULE s_hModD3D11 = NULL;
static LPD3D11CREATEDEVICE sFnPtr_D3D11CreateDevice = NULL;
static LPD3D11CREATEDEVICEANDSWAPCHAIN sFnPtr_D3D11CreateDeviceAndSwapChain = NULL;
// unload the D3D10 DLLs
static bool dynlinkUnloadD3D11API(void)
{
if (s_hModDXGI)
{
FreeLibrary(s_hModDXGI);
s_hModDXGI = NULL;
}
if (s_hModD3D11)
{
FreeLibrary(s_hModD3D11);
s_hModD3D11 = NULL;
}
return true;
}
// Dynamically load the D3D11 DLLs loaded and map the function pointers
static bool dynlinkLoadD3D11API(void)
{
// If both modules are non-NULL, this function has already been called. Note
// that this doesn't guarantee that all ProcAddresses were found.
if (s_hModD3D11 != NULL && s_hModDXGI != NULL)
{
return true;
}
#if 1
// This may fail if Direct3D 11 isn't installed
s_hModD3D11 = LoadLibrary("d3d11.dll");
if (s_hModD3D11 != NULL)
{
sFnPtr_D3D11CreateDevice = (LPD3D11CREATEDEVICE)GetProcAddress(s_hModD3D11, "D3D11CreateDevice");
sFnPtr_D3D11CreateDeviceAndSwapChain = (LPD3D11CREATEDEVICEANDSWAPCHAIN)GetProcAddress(s_hModD3D11, "D3D11CreateDeviceAndSwapChain");
}
else
{
printf("\nLoad d3d11.dll failed\n");
fflush(0);
}
if (!sFnPtr_CreateDXGIFactory)
{
s_hModDXGI = LoadLibrary("dxgi.dll");
if (s_hModDXGI)
{
sFnPtr_CreateDXGIFactory = (LPCREATEDXGIFACTORY)GetProcAddress(s_hModDXGI, "CreateDXGIFactory1");
}
return (s_hModDXGI != NULL) && (s_hModD3D11 != NULL);
}
return (s_hModD3D11 != NULL);
#else
sFnPtr_D3D11CreateDevice = (LPD3D11CREATEDEVICE)D3D11CreateDeviceAndSwapChain;
sFnPtr_D3D11CreateDeviceAndSwapChain = (LPD3D11CREATEDEVICEANDSWAPCHAIN)D3D11CreateDeviceAndSwapChain;
//sFnPtr_D3DX11CreateEffectFromMemory = ( LPD3DX11CREATEEFFECTFROMMEMORY )D3DX11CreateEffectFromMemory;
sFnPtr_D3DX11CompileFromMemory = (LPD3DX11COMPILEFROMMEMORY)D3DX11CompileFromMemory;
sFnPtr_CreateDXGIFactory = (LPCREATEDXGIFACTORY)CreateDXGIFactory;
return true;
#endif
return true;
}
#endif

View File

@@ -579,24 +579,12 @@ static const char *_cudaGetErrorEnum(NppStatus error) {
}
#endif
#ifdef __DRIVER_TYPES_H__
#ifndef DEVICE_RESET
#define DEVICE_RESET cudaDeviceReset();
#endif
#else
#ifndef DEVICE_RESET
#define DEVICE_RESET
#endif
#endif
template <typename T>
void check(T result, char const *const func, const char *const file,
int const line) {
if (result) {
fprintf(stderr, "CUDA error at %s:%d code=%d(%s) \"%s\" \n", file, line,
static_cast<unsigned int>(result), _cudaGetErrorEnum(result), func);
DEVICE_RESET
// Make sure we call CUDA Device Reset before exiting
exit(EXIT_FAILURE);
}
}
@@ -619,7 +607,6 @@ inline void __getLastCudaError(const char *errorMessage, const char *file,
" %s : (%d) %s.\n",
file, line, errorMessage, static_cast<int>(err),
cudaGetErrorString(err));
DEVICE_RESET
exit(EXIT_FAILURE);
}
}
@@ -696,6 +683,50 @@ inline int _ConvertSMVer2Cores(int major, int minor) {
major, minor, nGpuArchCoresPerSM[index - 1].Cores);
return nGpuArchCoresPerSM[index - 1].Cores;
}
inline const char* _ConvertSMVer2ArchName(int major, int minor) {
// Defines for GPU Architecture types (using the SM version to determine
// the GPU Arch name)
typedef struct {
int SM; // 0xMm (hexidecimal notation), M = SM Major version,
// and m = SM minor version
const char* name;
} sSMtoArchName;
sSMtoArchName nGpuArchNameSM[] = {
{0x30, "Kepler"},
{0x32, "Kepler"},
{0x35, "Kepler"},
{0x37, "Kepler"},
{0x50, "Maxwell"},
{0x52, "Maxwell"},
{0x53, "Maxwell"},
{0x60, "Pascal"},
{0x61, "Pascal"},
{0x62, "Pascal"},
{0x70, "Volta"},
{0x72, "Xavier"},
{0x75, "Turing"},
{-1, "Graphics Device"}};
int index = 0;
while (nGpuArchNameSM[index].SM != -1) {
if (nGpuArchNameSM[index].SM == ((major << 4) + minor)) {
return nGpuArchNameSM[index].name;
}
index++;
}
// If we don't find the values, we default use the previous one
// to run properly
printf(
"MapSMtoArchName for SM %d.%d is undefined."
" Default to use %s\n",
major, minor, nGpuArchNameSM[index - 1].name);
return nGpuArchNameSM[index - 1].name;
}
// end of GPU Architecture definitions
#ifdef __CUDA_RUNTIME_H__
@@ -727,23 +758,24 @@ inline int gpuDeviceInit(int devID) {
return -devID;
}
cudaDeviceProp deviceProp;
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, devID));
if (deviceProp.computeMode == cudaComputeModeProhibited) {
int computeMode = -1, major = 0, minor = 0;
checkCudaErrors(cudaDeviceGetAttribute(&computeMode, cudaDevAttrComputeMode, devID));
checkCudaErrors(cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, devID));
checkCudaErrors(cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, devID));
if (computeMode == cudaComputeModeProhibited) {
fprintf(stderr,
"Error: device is running in <Compute Mode "
"Prohibited>, no threads can use cudaSetDevice().\n");
return -1;
}
if (deviceProp.major < 1) {
if (major < 1) {
fprintf(stderr, "gpuDeviceInit(): GPU device does not support CUDA.\n");
exit(EXIT_FAILURE);
}
checkCudaErrors(cudaSetDevice(devID));
printf("gpuDeviceInit() CUDA Device [%d]: \"%s\n", devID, deviceProp.name);
printf("gpuDeviceInit() CUDA Device [%d]: \"%s\n", devID, _ConvertSMVer2ArchName(major, minor));
return devID;
}
@@ -756,7 +788,6 @@ inline int gpuGetMaxGflopsDeviceId() {
int devices_prohibited = 0;
uint64_t max_compute_perf = 0;
cudaDeviceProp deviceProp;
checkCudaErrors(cudaGetDeviceCount(&device_count));
if (device_count == 0) {
@@ -770,20 +801,24 @@ inline int gpuGetMaxGflopsDeviceId() {
current_device = 0;
while (current_device < device_count) {
cudaGetDeviceProperties(&deviceProp, current_device);
int computeMode = -1, major = 0, minor = 0;
checkCudaErrors(cudaDeviceGetAttribute(&computeMode, cudaDevAttrComputeMode, current_device));
checkCudaErrors(cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, current_device));
checkCudaErrors(cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, current_device));
// If this GPU is not running on Compute Mode prohibited,
// then we can add it to the list
if (deviceProp.computeMode != cudaComputeModeProhibited) {
if (deviceProp.major == 9999 && deviceProp.minor == 9999) {
if (computeMode != cudaComputeModeProhibited) {
if (major == 9999 && minor == 9999) {
sm_per_multiproc = 1;
} else {
sm_per_multiproc =
_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor);
_ConvertSMVer2Cores(major, minor);
}
uint64_t compute_perf = (uint64_t)deviceProp.multiProcessorCount *
sm_per_multiproc * deviceProp.clockRate;
int multiProcessorCount = 0, clockRate = 0;
checkCudaErrors(cudaDeviceGetAttribute(&multiProcessorCount, cudaDevAttrMultiProcessorCount, current_device));
checkCudaErrors(cudaDeviceGetAttribute(&clockRate, cudaDevAttrClockRate, current_device));
uint64_t compute_perf = (uint64_t)multiProcessorCount * sm_per_multiproc * clockRate;
if (compute_perf > max_compute_perf) {
max_compute_perf = compute_perf;
@@ -808,7 +843,6 @@ inline int gpuGetMaxGflopsDeviceId() {
// Initialization code to find the best CUDA Device
inline int findCudaDevice(int argc, const char **argv) {
cudaDeviceProp deviceProp;
int devID = 0;
// If the command-line has a device number specified, use it
@@ -830,9 +864,12 @@ inline int findCudaDevice(int argc, const char **argv) {
// Otherwise pick the device with highest Gflops/s
devID = gpuGetMaxGflopsDeviceId();
checkCudaErrors(cudaSetDevice(devID));
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, devID));
printf("GPU Device %d: \"%s\" with compute capability %d.%d\n\n", devID,
deviceProp.name, deviceProp.major, deviceProp.minor);
int major = 0, minor = 0;
checkCudaErrors(cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, devID));
checkCudaErrors(cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, devID));
printf("GPU Device %d: \"%s\" with compute capability %d.%d\n\n",
devID, _ConvertSMVer2ArchName(major, minor), major, minor);
}
return devID;
@@ -843,7 +880,6 @@ inline int findIntegratedGPU() {
int device_count = 0;
int devices_prohibited = 0;
cudaDeviceProp deviceProp;
checkCudaErrors(cudaGetDeviceCount(&device_count));
if (device_count == 0) {
@@ -853,17 +889,19 @@ inline int findIntegratedGPU() {
// Find the integrated GPU which is compute capable
while (current_device < device_count) {
cudaGetDeviceProperties(&deviceProp, current_device);
int computeMode = -1, integrated = -1;
checkCudaErrors(cudaDeviceGetAttribute(&computeMode, cudaDevAttrComputeMode, current_device));
checkCudaErrors(cudaDeviceGetAttribute(&integrated, cudaDevAttrIntegrated, current_device));
// If GPU is integrated and is not running on Compute Mode prohibited,
// then cuda can map to GLES resource
if (deviceProp.integrated &&
(deviceProp.computeMode != cudaComputeModeProhibited)) {
if (integrated && (computeMode != cudaComputeModeProhibited)) {
checkCudaErrors(cudaSetDevice(current_device));
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, current_device));
int major = 0, minor = 0;
checkCudaErrors(cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, current_device));
checkCudaErrors(cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, current_device));
printf("GPU Device %d: \"%s\" with compute capability %d.%d\n\n",
current_device, deviceProp.name, deviceProp.major,
deviceProp.minor);
current_device, _ConvertSMVer2ArchName(major, minor), major, minor);
return current_device;
} else {
@@ -885,19 +923,18 @@ inline int findIntegratedGPU() {
// General check for CUDA GPU SM Capabilities
inline bool checkCudaCapabilities(int major_version, int minor_version) {
cudaDeviceProp deviceProp;
deviceProp.major = 0;
deviceProp.minor = 0;
int dev;
int major = 0, minor = 0;
checkCudaErrors(cudaGetDevice(&dev));
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, dev));
checkCudaErrors(cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, dev));
checkCudaErrors(cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, dev));
if ((deviceProp.major > major_version) ||
(deviceProp.major == major_version &&
deviceProp.minor >= minor_version)) {
if ((major > major_version) ||
(major == major_version &&
minor >= minor_version)) {
printf(" Device %d: <%16s >, Compute SM %d.%d detected\n", dev,
deviceProp.name, deviceProp.major, deviceProp.minor);
_ConvertSMVer2ArchName(major, minor), major, minor);
return true;
} else {
printf(

View File

@@ -29,150 +29,534 @@
#include <cstdlib>
#include <string>
int sharedMemoryCreate(const char *name, size_t sz, sharedMemoryInfo *info)
{
int sharedMemoryCreate(const char *name, size_t sz, sharedMemoryInfo *info) {
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
info->size = sz;
info->shmHandle = CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
(DWORD)sz,
name);
if (info->shmHandle == 0) {
return GetLastError();
}
info->size = sz;
info->shmHandle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE, 0, (DWORD)sz, name);
if (info->shmHandle == 0) {
return GetLastError();
}
info->addr = MapViewOfFile(info->shmHandle, FILE_MAP_ALL_ACCESS, 0, 0, sz);
if (info->addr == NULL) {
return GetLastError();
}
info->addr = MapViewOfFile(info->shmHandle, FILE_MAP_ALL_ACCESS, 0, 0, sz);
if (info->addr == NULL) {
return GetLastError();
}
return 0;
return 0;
#else
int status = 0;
int status = 0;
info->size = sz;
info->size = sz;
info->shmFd = shm_open(name, O_RDWR | O_CREAT, 0777);
if (info->shmFd < 0) {
return errno;
}
info->shmFd = shm_open(name, O_RDWR | O_CREAT, 0777);
if (info->shmFd < 0) {
return errno;
}
status = ftruncate(info->shmFd, sz);
if (status != 0) {
return status;
}
status = ftruncate(info->shmFd, sz);
if (status != 0) {
return status;
}
info->addr = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_SHARED, info->shmFd, 0);
if (info->addr == NULL) {
return errno;
}
info->addr = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_SHARED, info->shmFd, 0);
if (info->addr == NULL) {
return errno;
}
return 0;
return 0;
#endif
}
int sharedMemoryOpen(const char *name, size_t sz, sharedMemoryInfo *info)
{
int sharedMemoryOpen(const char *name, size_t sz, sharedMemoryInfo *info) {
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
info->size = sz;
info->size = sz;
info->shmHandle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, name);
if (info->shmHandle == 0) {
return GetLastError();
}
info->shmHandle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, name);
if (info->shmHandle == 0) {
return GetLastError();
}
info->addr = MapViewOfFile(info->shmHandle, FILE_MAP_ALL_ACCESS, 0, 0, sz);
if (info->addr == NULL) {
return GetLastError();
}
info->addr = MapViewOfFile(info->shmHandle, FILE_MAP_ALL_ACCESS, 0, 0, sz);
if (info->addr == NULL) {
return GetLastError();
}
return 0;
return 0;
#else
info->size = sz;
info->size = sz;
info->shmFd = shm_open(name, O_RDWR, 0777);
if (info->shmFd < 0) {
return errno;
}
info->shmFd = shm_open(name, O_RDWR, 0777);
if (info->shmFd < 0) {
return errno;
}
info->addr = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_SHARED, info->shmFd, 0);
if (info->addr == NULL) {
return errno;
}
info->addr = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_SHARED, info->shmFd, 0);
if (info->addr == NULL) {
return errno;
}
return 0;
return 0;
#endif
}
void sharedMemoryClose(sharedMemoryInfo *info)
{
void sharedMemoryClose(sharedMemoryInfo *info) {
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
if (info->addr) {
UnmapViewOfFile(info->addr);
}
if (info->shmHandle) {
CloseHandle(info->shmHandle);
}
if (info->addr) {
UnmapViewOfFile(info->addr);
}
if (info->shmHandle) {
CloseHandle(info->shmHandle);
}
#else
if (info->addr) {
munmap(info->addr, info->size);
}
if (info->shmFd) {
close(info->shmFd);
}
if (info->addr) {
munmap(info->addr, info->size);
}
if (info->shmFd) {
close(info->shmFd);
}
#endif
}
int spawnProcess(Process *process, const char *app, char * const *args)
{
int spawnProcess(Process *process, const char *app, char *const *args) {
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
STARTUPINFO si = {0};
BOOL status;
size_t arglen = 0;
size_t argIdx = 0;
std::string arg_string;
memset(process, 0, sizeof(*process));
STARTUPINFO si = {0};
BOOL status;
size_t arglen = 0;
size_t argIdx = 0;
std::string arg_string;
memset(process, 0, sizeof(*process));
while (*args) {
arg_string.append(*args).append(1, ' ');
args++;
}
while (*args) {
arg_string.append(*args).append(1, ' ');
args++;
}
status = CreateProcess(app, LPSTR(arg_string.c_str()), NULL, NULL, FALSE, 0, NULL, NULL, &si, process);
status = CreateProcess(app, LPSTR(arg_string.c_str()), NULL, NULL, FALSE, 0,
NULL, NULL, &si, process);
return status ? 0 : GetLastError();
return status ? 0 : GetLastError();
#else
*process = fork();
if (*process == 0) {
if (0 > execvp(app, args)) {
return errno;
}
*process = fork();
if (*process == 0) {
if (0 > execvp(app, args)) {
return errno;
}
else if (*process < 0) {
return errno;
}
return 0;
} else if (*process < 0) {
return errno;
}
return 0;
#endif
}
int waitProcess(Process *process)
{
int waitProcess(Process *process) {
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
DWORD exitCode;
WaitForSingleObject(process->hProcess, INFINITE);
GetExitCodeProcess(process->hProcess, &exitCode);
CloseHandle(process->hProcess);
CloseHandle(process->hThread);
return (int)exitCode;
DWORD exitCode;
WaitForSingleObject(process->hProcess, INFINITE);
GetExitCodeProcess(process->hProcess, &exitCode);
CloseHandle(process->hProcess);
CloseHandle(process->hThread);
return (int)exitCode;
#else
int status = 0;
do {
if (0 > waitpid(*process, &status, 0)) {
return errno;
}
} while (!WIFEXITED(status));
return WEXITSTATUS(status);
int status = 0;
do {
if (0 > waitpid(*process, &status, 0)) {
return errno;
}
} while (!WIFEXITED(status));
return WEXITSTATUS(status);
#endif
}
#if defined(__linux__)
int ipcCreateSocket(ipcHandle *&handle, const char *name,
const std::vector<Process> &processes) {
int server_fd;
struct sockaddr_un servaddr;
handle = new ipcHandle;
memset(handle, 0, sizeof(*handle));
handle->socket = -1;
handle->socketName = NULL;
// Creating socket file descriptor
if ((server_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == 0) {
perror("IPC failure: Socket creation failed");
return -1;
}
unlink(name);
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_UNIX;
size_t len = strlen(name);
if (len > (sizeof(servaddr.sun_path) - 1)) {
perror("IPC failure: Cannot bind provided name to socket. Name too large");
return -1;
}
strncpy(servaddr.sun_path, name, len);
if (bind(server_fd, (struct sockaddr *)&servaddr, SUN_LEN(&servaddr)) < 0) {
perror("IPC failure: Binding socket failed");
return -1;
}
handle->socketName = new char[strlen(name) + 1];
strcpy(handle->socketName, name);
handle->socket = server_fd;
return 0;
}
int ipcOpenSocket(ipcHandle *&handle) {
int sock = 0;
struct sockaddr_un cliaddr;
handle = new ipcHandle;
memset(handle, 0, sizeof(*handle));
if ((sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
perror("IPC failure:Socket creation error");
return -1;
}
bzero(&cliaddr, sizeof(cliaddr));
cliaddr.sun_family = AF_UNIX;
char temp[10];
// Create unique name for the socket.
sprintf(temp, "%u", getpid());
strcpy(cliaddr.sun_path, temp);
if (bind(sock, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0) {
perror("IPC failure: Binding socket failed");
return -1;
}
handle->socket = sock;
handle->socketName = new char[strlen(temp) + 1];
strcpy(handle->socketName, temp);
return 0;
}
int ipcCloseSocket(ipcHandle *handle) {
if (!handle) {
return -1;
}
if (handle->socketName) {
unlink(handle->socketName);
delete[] handle->socketName;
}
close(handle->socket);
delete handle;
return 0;
}
int ipcRecvShareableHandle(ipcHandle *handle, ShareableHandle *shHandle) {
struct msghdr msg = {0};
struct iovec iov[1];
struct cmsghdr cm;
// Union to guarantee alignment requirements for control array
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
ssize_t n;
int receivedfd;
char dummy_buffer[1];
ssize_t sendResult;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
iov[0].iov_base = (void *)dummy_buffer;
iov[0].iov_len = sizeof(dummy_buffer);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
if ((n = recvmsg(handle->socket, &msg, 0)) <= 0) {
perror("IPC failure: Receiving data over socket failed");
return -1;
}
if (((cmptr = CMSG_FIRSTHDR(&msg)) != NULL) &&
(cmptr->cmsg_len == CMSG_LEN(sizeof(int)))) {
if ((cmptr->cmsg_level != SOL_SOCKET) || (cmptr->cmsg_type != SCM_RIGHTS)) {
return -1;
}
memmove(&receivedfd, CMSG_DATA(cmptr), sizeof(receivedfd));
*(int *)shHandle = receivedfd;
} else {
return -1;
}
return 0;
}
int ipcRecvDataFromClient(ipcHandle *serverHandle, void *data, size_t size) {
ssize_t readResult;
struct sockaddr_un cliaddr;
socklen_t len = sizeof(cliaddr);
readResult = recvfrom(serverHandle->socket, data, size, 0,
(struct sockaddr *)&cliaddr, &len);
if (readResult == -1) {
perror("IPC failure: Receiving data over socket failed");
return -1;
}
return 0;
}
int ipcSendDataToServer(ipcHandle *handle, const char *serverName,
const void *data, size_t size) {
ssize_t sendResult;
struct sockaddr_un serveraddr;
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sun_family = AF_UNIX;
strncpy(serveraddr.sun_path, serverName, sizeof(serveraddr.sun_path) - 1);
sendResult = sendto(handle->socket, data, size, 0,
(struct sockaddr *)&serveraddr, sizeof(serveraddr));
if (sendResult <= 0) {
perror("IPC failure: Sending data over socket failed");
}
return 0;
}
int ipcSendShareableHandle(ipcHandle *handle,
const std::vector<ShareableHandle> &shareableHandles,
Process process, int data) {
struct msghdr msg;
struct iovec iov[1];
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
ssize_t readResult;
struct sockaddr_un cliaddr;
socklen_t len = sizeof(cliaddr);
// Construct client address to send this SHareable handle to
bzero(&cliaddr, sizeof(cliaddr));
cliaddr.sun_family = AF_UNIX;
char temp[10];
sprintf(temp, "%u", process);
strcpy(cliaddr.sun_path, temp);
len = sizeof(cliaddr);
// Send corresponding shareable handle to the client
int sendfd = (int)shareableHandles[data];
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
cmptr = CMSG_FIRSTHDR(&msg);
cmptr->cmsg_len = CMSG_LEN(sizeof(int));
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
memmove(CMSG_DATA(cmptr), &sendfd, sizeof(sendfd));
msg.msg_name = (void *)&cliaddr;
msg.msg_namelen = sizeof(struct sockaddr_un);
iov[0].iov_base = (void *)"";
iov[0].iov_len = 1;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
ssize_t sendResult = sendmsg(handle->socket, &msg, 0);
if (sendResult <= 0) {
perror("IPC failure: Sending data over socket failed");
return -1;
}
return 0;
}
int ipcSendShareableHandles(
ipcHandle *handle, const std::vector<ShareableHandle> &shareableHandles,
const std::vector<Process> &processes) {
// Send all shareable handles to every single process.
for (int i = 0; i < shareableHandles.size(); i++) {
for (int j = 0; j < processes.size(); j++) {
checkIpcErrors(
ipcSendShareableHandle(handle, shareableHandles, processes[j], i));
}
}
return 0;
}
int ipcRecvShareableHandles(ipcHandle *handle,
std::vector<ShareableHandle> &shareableHandles) {
for (int i = 0; i < shareableHandles.size(); i++) {
checkIpcErrors(ipcRecvShareableHandle(handle, &shareableHandles[i]));
}
return 0;
}
int ipcCloseShareableHandle(ShareableHandle shHandle) {
return close(shHandle);
}
#elif defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
// Generic name to build individual Mailslot names by appending process ids.
LPTSTR SlotName = (LPTSTR)TEXT("\\\\.\\mailslot\\sample_mailslot_");
int ipcCreateSocket(ipcHandle *&handle, const char *name,
const std::vector<Process> &processes) {
handle = new ipcHandle;
handle->hMailslot.resize(processes.size());
// Open Mailslots of all clients and store respective handles.
for (int i = 0; i < handle->hMailslot.size(); ++i) {
std::basic_string<TCHAR> childSlotName(SlotName);
char tempBuf[20];
_itoa_s(processes[i].dwProcessId, tempBuf, 10);
childSlotName += TEXT(tempBuf);
HANDLE hFile =
CreateFile(TEXT(childSlotName.c_str()), GENERIC_WRITE, FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("IPC failure: Opening Mailslot by CreateFile failed with %d\n",
GetLastError());
return -1;
}
handle->hMailslot[i] = hFile;
}
return 0;
}
int ipcOpenSocket(ipcHandle *&handle) {
handle = new ipcHandle;
HANDLE hSlot;
std::basic_string<TCHAR> clientSlotName(SlotName);
char tempBuf[20];
_itoa_s(GetCurrentProcessId(), tempBuf, 10);
clientSlotName += TEXT(tempBuf);
hSlot = CreateMailslot((LPSTR)clientSlotName.c_str(), 0,
MAILSLOT_WAIT_FOREVER, (LPSECURITY_ATTRIBUTES)NULL);
if (hSlot == INVALID_HANDLE_VALUE) {
printf("IPC failure: CreateMailslot failed for client with %d\n",
GetLastError());
return -1;
}
handle->hMailslot.push_back(hSlot);
return 0;
}
int ipcSendData(HANDLE mailslot, const void *data, size_t sz) {
BOOL result;
DWORD cbWritten;
result = WriteFile(mailslot, data, (DWORD)sz, &cbWritten, (LPOVERLAPPED)NULL);
if (!result) {
printf("IPC failure: WriteFile failed with %d.\n", GetLastError());
return -1;
}
return 0;
}
int ipcRecvData(ipcHandle *handle, void *data, size_t sz) {
DWORD cbMessage, cMessage, cbRead;
BOOL fResult;
cbMessage = cMessage = cbRead = 0;
HANDLE mailslot = handle->hMailslot[0];
pollMailSlot:
fResult = GetMailslotInfo(mailslot, (LPDWORD)NULL, &cbMessage, &cMessage,
(LPDWORD)NULL);
if (!fResult) {
printf("IPC failure: GetMailslotInfo failed with %d.\n", GetLastError());
return -1;
}
if (cbMessage == MAILSLOT_NO_MESSAGE) {
goto pollMailSlot;
}
while (cMessage != 0) {
fResult = ReadFile(mailslot, data, (DWORD)sz, &cbRead, NULL);
if (!fResult) {
printf("IPC failure: ReadFile failed with %d.\n", GetLastError());
return -1;
}
fResult = GetMailslotInfo(mailslot, (LPDWORD)NULL, &cbMessage, &cMessage,
(LPDWORD)NULL);
if (!fResult) {
printf("IPC failure: GetMailslotInfo failed (%d)\n", GetLastError());
return -1;
}
}
return 0;
}
int ipcSendShareableHandles(
ipcHandle *handle, const std::vector<ShareableHandle> &shareableHandles,
const std::vector<Process> &processes) {
// Send all shareable handles to every single process.
for (int i = 0; i < processes.size(); i++) {
HANDLE hProcess =
OpenProcess(PROCESS_DUP_HANDLE, FALSE, processes[i].dwProcessId);
if (hProcess == INVALID_HANDLE_VALUE) {
printf("IPC failure: OpenProcess failed (%d)\n", GetLastError());
return -1;
}
for (int j = 0; j < shareableHandles.size(); j++) {
HANDLE hDup = INVALID_HANDLE_VALUE;
// Duplicate the handle into the target process's space
if (!DuplicateHandle(GetCurrentProcess(), shareableHandles[j], hProcess,
&hDup, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
printf("IPC failure: DuplicateHandle failed (%d)\n", GetLastError());
return -1;
}
checkIpcErrors(ipcSendData(handle->hMailslot[i], &hDup, sizeof(HANDLE)));
}
CloseHandle(hProcess);
}
return 0;
}
int ipcRecvShareableHandles(ipcHandle *handle,
std::vector<ShareableHandle> &shareableHandles) {
for (int i = 0; i < shareableHandles.size(); i++) {
checkIpcErrors(
ipcRecvData(handle, &shareableHandles[i], sizeof(shareableHandles[i])));
}
return 0;
}
int ipcCloseSocket(ipcHandle *handle) {
for (int i = 0; i < handle->hMailslot.size(); i++) {
CloseHandle(handle->hMailslot[i]);
}
delete handle;
return 0;
}
int ipcCloseShareableHandle(ShareableHandle shHandle) {
CloseHandle(shHandle);
return 0;
}
#endif

View File

@@ -33,13 +33,26 @@
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <iostream>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#include <sddl.h>
#include <aclapi.h>
#include <winternl.h>
#else
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <memory.h>
#include <sys/un.h>
#endif
#include <vector>
typedef struct sharedMemoryInfo_st {
void *addr;
@@ -68,4 +81,40 @@ int spawnProcess(Process *process, const char *app, char * const *args);
int waitProcess(Process *process);
#define checkIpcErrors(ipcFuncResult) \
if (ipcFuncResult == -1) { fprintf(stderr, "Failure at %u %s\n", __LINE__, __FILE__); exit(EXIT_FAILURE); }
#if defined(__linux__)
struct ipcHandle_st {
int socket;
char *socketName;
};
typedef int ShareableHandle;
#elif defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
struct ipcHandle_st {
std::vector<HANDLE> hMailslot; // 1 Handle in case of child and `num children` Handles for parent.
};
typedef HANDLE ShareableHandle;
#endif
typedef struct ipcHandle_st ipcHandle;
int
ipcCreateSocket(ipcHandle *&handle, const char *name, const std::vector<Process>& processes);
int
ipcOpenSocket(ipcHandle *&handle);
int
ipcCloseSocket(ipcHandle *handle);
int
ipcRecvShareableHandles(ipcHandle *handle, std::vector<ShareableHandle>& shareableHandles);
int
ipcSendShareableHandles(ipcHandle *handle, const std::vector<ShareableHandle>& shareableHandles, const std::vector<Process>& processes);
int
ipcCloseShareableHandle(ShareableHandle shHandle);
#endif // HELPER_MULTIPROCESS_H

View File

@@ -297,6 +297,19 @@ int writeBMPi(const char *filename, const unsigned char *d_RGB, int pitch,
return 0;
}
int inputDirExists(const char *pathname) {
struct stat info;
if (stat(pathname, &info) != 0) {
return 0; // Directory does not exists
} else if (info.st_mode & S_IFDIR) {
// is a directory
return 1;
} else {
// is not a directory
return 0;
}
}
int readInput(const std::string &sInputPath,
std::vector<std::string> &filelist) {
int error_code = 1;
@@ -315,15 +328,18 @@ int readInput(const std::string &sInputPath,
if (dir_handle) {
error_code = 0;
while ((dir = readdir(dir_handle)) != NULL) {
if (dir->d_type == DT_REG) {
std::string sFileName = sInputPath + dir->d_name;
filelist.push_back(sFileName);
} else if (dir->d_type == DT_DIR) {
std::string sFileName = sInputPath + dir->d_name;
if (inputDirExists(sFileName.c_str()))
{
std::string sname = dir->d_name;
if (sname != "." && sname != "..") {
readInput(sInputPath + sname + "/", filelist);
}
}
else
{
filelist.push_back(sFileName);
}
}
closedir(dir_handle);
} else {
@@ -360,18 +376,6 @@ int readInput(const std::string &sInputPath,
return 0;
}
int inputDirExists(const char *pathname) {
struct stat info;
if (stat(pathname, &info) != 0) {
return 0; // Directory does not exists
} else if (info.st_mode & S_IFDIR) {
// is a directory
return 1;
} else {
// is not a directory
return 0;
}
}
int getInputDir(std::string &input_dir, const char *executable_path) {
int found = 0;
@@ -399,6 +403,7 @@ int getInputDir(std::string &input_dir, const char *executable_path) {
"../../../Samples/<executable_name>/images",
"../../Samples/<executable_name>/images"};
for (unsigned int i = 0; i < sizeof(searchPath) / sizeof(char *); ++i) {
std::string pathname(searchPath[i]);
size_t executable_name_pos = pathname.find("<executable_name>");
@@ -420,4 +425,4 @@ int getInputDir(std::string &input_dir, const char *executable_path) {
return found;
}
#endif
#endif

View File

@@ -0,0 +1,124 @@
/* Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
////////////////////////////////////////////////////////////////////////////////
//
// Utility funcs to wrap up saving a surface or the back buffer as a PPM file
// In addition, wraps up a threshold comparision of two PPMs.
//
// These functions are designed to be used to implement an automated QA testing for SDK samples.
//
// Author: Bryan Dudash
// Email: sdkfeedback@nvidia.com
//
// Copyright (c) NVIDIA Corporation. All rights reserved.
////////////////////////////////////////////////////////////////////////////////
#include <helper_functions.h>
#include <rendercheck_d3d11.h>
HRESULT CheckRenderD3D11::ActiveRenderTargetToPPM(ID3D11Device *pDevice, const char *zFileName)
{
ID3D11DeviceContext *pDeviceCtxt;
pDevice->GetImmediateContext(&pDeviceCtxt);
ID3D11RenderTargetView *pRTV = NULL;
pDeviceCtxt->OMGetRenderTargets(1,&pRTV,NULL);
ID3D11Resource *pSourceResource = NULL;
pRTV->GetResource(&pSourceResource);
return ResourceToPPM(pDevice,pSourceResource,zFileName);
}
HRESULT CheckRenderD3D11::ResourceToPPM(ID3D11Device *pDevice, ID3D11Resource *pResource, const char *zFileName)
{
ID3D11DeviceContext *pDeviceCtxt;
pDevice->GetImmediateContext(&pDeviceCtxt);
D3D11_RESOURCE_DIMENSION rType;
pResource->GetType(&rType);
if (rType != D3D11_RESOURCE_DIMENSION_TEXTURE2D)
{
printf("SurfaceToPPM: pResource is not a 2D texture! Aborting...\n");
return E_FAIL;
}
ID3D11Texture2D *pSourceTexture = (ID3D11Texture2D *)pResource;
ID3D11Texture2D *pTargetTexture = NULL;
D3D11_TEXTURE2D_DESC desc;
pSourceTexture->GetDesc(&desc);
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.Usage = D3D11_USAGE_STAGING;
if (FAILED(pDevice->CreateTexture2D(&desc,NULL,&pTargetTexture)))
{
printf("SurfaceToPPM: Unable to create target Texture resoruce! Aborting... \n");
return E_FAIL;
}
pDeviceCtxt->CopyResource(pTargetTexture,pSourceTexture);
D3D11_MAPPED_SUBRESOURCE mappedTex2D;
pDeviceCtxt->Map(pTargetTexture, 0, D3D11_MAP_READ,0,&mappedTex2D);
// Need to convert from dx pitch to pitch=width
unsigned char *pPPMData = new unsigned char[desc.Width*desc.Height*4];
for (unsigned int iHeight = 0; iHeight<desc.Height; iHeight++)
{
memcpy(&(pPPMData[iHeight*desc.Width*4]),(unsigned char *)(mappedTex2D.pData)+iHeight*mappedTex2D.RowPitch,desc.Width*4);
}
pDeviceCtxt->Unmap(pTargetTexture, 0);
// Prepends the PPM header info and bumps byte data afterwards
sdkSavePPM4ub(zFileName, pPPMData, desc.Width, desc.Height);
delete [] pPPMData;
pTargetTexture->Release();
return S_OK;
}
bool CheckRenderD3D11::PPMvsPPM(const char *src_file, const char *ref_file, const char *exec_path,
const float epsilon, const float threshold)
{
char *ref_file_path = sdkFindFilePath(ref_file, exec_path);
if (ref_file_path == NULL)
{
printf("CheckRenderD3D11::PPMvsPPM unable to find <%s> in <%s> Aborting comparison!\n", ref_file, exec_path);
printf(">>> Check info.xml and [project//data] folder <%s> <<<\n", ref_file);
printf("Aborting comparison!\n");
printf(" FAILURE!\n");
return false;
}
return sdkComparePPM(src_file,ref_file_path,epsilon,threshold,true) == true;
}

View File

@@ -0,0 +1,52 @@
/* Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#ifndef _RENDERCHECK_D3D11_H_
#define _RENDERCHECK_D3D11_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <d3d11.h>
class CheckRenderD3D11
{
public:
CheckRenderD3D11() {}
static HRESULT ActiveRenderTargetToPPM(ID3D11Device *pDevice, const char *zFileName);
static HRESULT ResourceToPPM(ID3D11Device *pDevice, ID3D11Resource *pResource, const char *zFileName);
static bool PPMvsPPM(const char *src_file, const char *ref_file, const char *exec_path,
const float epsilon, const float threshold = 0.0f);
};
#endif