gfx/amd, gfx/intel: the FidelityFX API and XeSS loaders

Run-time loaders for AMD FSR through the FidelityFX API and Intel XeSS,
C89, beside the NGX one: the SDKs' descriptor and parameter layouts and
their type numbers declared here so no SDK header is needed, the
library looked for under the SDKs' file names in a directory of the
caller's and kept once found. The samples pin the layouts to the SDKs'
sizes and offsets on x86-64 and drive the loaders against stand-in
libraries that export the SDKs' entry points, on Linux and under
mingw/Wine.
This commit is contained in:
libretroadmin committed 2026-10-26 04:05:58 +01:00
1 parent aca5868b04
commit b6296db205
12 files changed
+2453

No files matched your search

+242
View File
@@ -0,0 +1,242 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (ffx_loader.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <string.h>
#include <compat/strl.h>
#include <file/file_path.h>
#include <retro_miscellaneous.h>
#include <string/stdstring.h>
#include <gfx/amd/ffx_loader.h>
#if defined(_WIN64) && !defined(__WINRT__) && !defined(_XBOX)
#define FFX_HAVE_LOADER 1
#include <windows.h>
#elif defined(__linux__) && (defined(__x86_64__) || defined(__aarch64__))
#define FFX_HAVE_LOADER 1
#include <dlfcn.h>
#endif
/* The files, in the order tried: SDK 2's loader (with the effect
* library beside it), then SDK 1.1's library of everything */
static const char *ffx_names_d3d12[] = {
#if defined(_WIN32)
"amd_fidelityfx_loader_dx12.dll", "amd_fidelityfx_dx12.dll"
#else
"libamd_fidelityfx_loader_dx12.so", "libamd_fidelityfx_dx12.so"
#endif
};
static const char *ffx_names_vulkan[] = {
#if defined(_WIN32)
"amd_fidelityfx_vk.dll"
#else
"libamd_fidelityfx_vk.so"
#endif
};
static unsigned ffx_names(enum ffx_api api, const char ***names)
{
if (api == FFX_API_VULKAN)
{
*names = ffx_names_vulkan;
return (unsigned)(sizeof(ffx_names_vulkan) / sizeof(ffx_names_vulkan[0]));
}
*names = ffx_names_d3d12;
return (unsigned)(sizeof(ffx_names_d3d12) / sizeof(ffx_names_d3d12[0]));
}
bool ffx_runtime_in(enum ffx_api api, const char *dir)
{
char path[DIR_MAX_LENGTH];
const char **names;
unsigned n, i;
if (string_is_empty(dir) || (unsigned)api >= FFX_API_LAST)
return false;
n = ffx_names(api, &names);
for (i = 0; i < n; i++)
{
fill_pathname_join_special(path, dir, names[i], sizeof(path));
if (path_is_valid(path))
return true;
}
return false;
}
#ifdef FFX_HAVE_LOADER
static ffx_lib_t ffx_libs[FFX_API_LAST];
static bool ffx_probed[FFX_API_LAST];
static char ffx_status[FFX_API_LAST][DIR_MAX_LENGTH + 128] = { "not probed", "not probed" };
typedef void (*ffx_proc_t)(void);
#if defined(_WIN32)
typedef HMODULE ffx_module_t;
static ffx_module_t ffx_open_file(const char *path)
{
/* (the loader's effect libraries sit beside it: found from its own directory) */
return LoadLibraryExA(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
}
static ffx_proc_t ffx_sym(ffx_module_t m, const char *name)
{
FARPROC p = GetProcAddress(m, name);
ffx_proc_t fn;
memcpy(&fn, &p, sizeof(fn));
return fn;
}
#else
typedef void *ffx_module_t;
static ffx_module_t ffx_open_file(const char *path)
{
return dlopen(path, RTLD_NOW | RTLD_LOCAL);
}
static ffx_proc_t ffx_sym(ffx_module_t m, const char *name)
{
ffx_proc_t fn;
void *sym = dlsym(m, name);
memcpy(&fn, &sym, sizeof(fn));
return fn;
}
#endif
#define FFX_SYM(dst, name) \
do { \
ffx_proc_t fn = ffx_sym(m, name); \
memcpy(&(dst), &fn, sizeof(fn)); \
if (!fn) \
missing = name; \
} while (0)
const ffx_lib_t *ffx_lib_get(enum ffx_api api, const char *dir)
{
if ((unsigned)api >= FFX_API_LAST)
return NULL;
if (!ffx_probed[api])
{
char path[DIR_MAX_LENGTH];
const char **names;
unsigned n, i;
ffx_module_t m = NULL;
ffx_lib_t *lib = &ffx_libs[api];
const char *missing = NULL;
char *status = ffx_status[api];
const size_t status_len = sizeof(ffx_status[api]);
if (string_is_empty(dir))
{
strlcpy(status, "no FSR directory", status_len);
return NULL;
}
n = ffx_names(api, &names);
for (i = 0; i < n && !m; i++)
{
fill_pathname_join_special(path, dir, names[i], sizeof(path));
if (path_is_valid(path))
m = ffx_open_file(path);
}
if (!m)
{
/* (not probed: the directory is a setting, tried again as set) */
strlcpy(status, "no FSR library (", status_len);
for (i = 0; i < n; i++)
{
strlcat(status, i ? " or " : "", status_len);
strlcat(status, names[i], status_len);
}
strlcat(status, ") in \"", status_len);
strlcat(status, dir, status_len);
strlcat(status, "\"", status_len);
return NULL;
}
ffx_probed[api] = true;
FFX_SYM(lib->create_context, "ffxCreateContext");
FFX_SYM(lib->destroy_context, "ffxDestroyContext");
FFX_SYM(lib->configure, "ffxConfigure");
FFX_SYM(lib->query, "ffxQuery");
FFX_SYM(lib->dispatch, "ffxDispatch");
if (missing)
{
strlcpy(status, "FSR library ", status_len);
strlcat(status, path, status_len);
strlcat(status, " lacks ", status_len);
strlcat(status, missing, status_len);
memset(lib, 0, sizeof(*lib));
/* (left loaded: a library is never unloaded under a device) */
return NULL;
}
lib->handle = (void*)m;
strlcpy(status, "FSR library ", status_len);
strlcat(status, path, status_len);
}
return ffx_libs[api].handle ? &ffx_libs[api] : NULL;
}
const char *ffx_lib_status(enum ffx_api api)
{
if ((unsigned)api >= FFX_API_LAST)
return "";
return ffx_status[api];
}
#else
const ffx_lib_t *ffx_lib_get(enum ffx_api api, const char *dir)
{
(void)api;
(void)dir;
return NULL;
}
const char *ffx_lib_status(enum ffx_api api)
{
(void)api;
return "no FSR on this platform";
}
#endif
unsigned ffx_lib_versions(const ffx_lib_t *lib, void *device,
uint64_t *ids, const char **names, unsigned cap)
{
ffx_query_versions_t q;
uint64_t count = 0;
if (!lib || !cap)
return 0;
memset(&q, 0, sizeof(q));
q.header.type = FFX_TYPE_GET_VERSIONS;
q.create_desc_type = FFX_TYPE_UPSCALE_CREATE;
q.device = device;
q.output_count = &count;
if (lib->query(NULL, &q.header) != FFX_RETURN_OK || !count)
return 0;
if (count > cap)
count = cap;
q.version_ids = ids;
q.version_names = names;
if (lib->query(NULL, &q.header) != FFX_RETURN_OK)
return 0;
return (unsigned)count;
}
+218
View File
@@ -0,0 +1,218 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (xess_loader.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include <compat/strl.h>
#include <file/file_path.h>
#include <retro_miscellaneous.h>
#include <string/stdstring.h>
#include <gfx/intel/xess_loader.h>
#if defined(_WIN64) && !defined(__WINRT__) && !defined(_XBOX)
#define XESS_HAVE_LOADER 1
#include <windows.h>
#elif defined(__linux__) && (defined(__x86_64__) || defined(__aarch64__))
#define XESS_HAVE_LOADER 1
#include <dlfcn.h>
#endif
#if defined(_WIN32)
#define XESS_LIBRARY "libxess.dll"
#else
#define XESS_LIBRARY "libxess.so"
#endif
bool xess_runtime_in(const char *dir)
{
char path[DIR_MAX_LENGTH];
if (string_is_empty(dir))
return false;
fill_pathname_join_special(path, dir, XESS_LIBRARY, sizeof(path));
return path_is_valid(path);
}
#ifdef XESS_HAVE_LOADER
static xess_lib_t xess_lib;
static bool xess_probed;
static char xess_status[DIR_MAX_LENGTH + 128] = "not probed";
typedef void (*xess_proc_t)(void);
#if defined(_WIN32)
typedef HMODULE xess_module_t;
static xess_module_t xess_open_file(const char *path)
{
return LoadLibraryExA(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
}
static xess_proc_t xess_sym(xess_module_t m, const char *name)
{
FARPROC p = GetProcAddress(m, name);
xess_proc_t fn;
memcpy(&fn, &p, sizeof(fn));
return fn;
}
#else
typedef void *xess_module_t;
static xess_module_t xess_open_file(const char *path)
{
return dlopen(path, RTLD_NOW | RTLD_LOCAL);
}
static xess_proc_t xess_sym(xess_module_t m, const char *name)
{
xess_proc_t fn;
void *sym = dlsym(m, name);
memcpy(&fn, &sym, sizeof(fn));
return fn;
}
#endif
/* An entry point; a missing one is named */
#define XESS_SYM(dst, name) \
do { \
xess_proc_t fn = xess_sym(m, name); \
memcpy(&(dst), &fn, sizeof(fn)); \
if (!fn) \
missing = name; \
} while (0)
static void xess_resolve_common(xess_module_t m, xess_common_api_t *a,
const char **missing_out)
{
const char *missing = NULL;
XESS_SYM(a->get_version, "xessGetVersion");
XESS_SYM(a->get_optimal_input_resolution, "xessGetOptimalInputResolution");
XESS_SYM(a->destroy_context, "xessDestroyContext");
XESS_SYM(a->set_jitter_scale, "xessSetJitterScale");
XESS_SYM(a->set_velocity_scale, "xessSetVelocityScale");
XESS_SYM(a->set_exposure_multiplier, "xessSetExposureMultiplier");
XESS_SYM(a->set_logging_callback, "xessSetLoggingCallback");
*missing_out = missing;
/* (XeSS 1.3 on: older libraries build their pipelines at init) */
XESS_SYM(a->get_pipeline_build_status, "xessGetPipelineBuildStatus");
}
static bool xess_resolve_d3d12(xess_module_t m, xess_d3d12_api_t *a)
{
const char *missing = NULL;
XESS_SYM(a->create_context, "xessD3D12CreateContext");
XESS_SYM(a->build_pipelines, "xessD3D12BuildPipelines");
XESS_SYM(a->init, "xessD3D12Init");
XESS_SYM(a->execute, "xessD3D12Execute");
return !missing;
}
static bool xess_resolve_vk(xess_module_t m, xess_vk_api_t *a)
{
const char *missing = NULL;
XESS_SYM(a->get_required_instance_extensions, "xessVKGetRequiredInstanceExtensions");
XESS_SYM(a->get_required_device_extensions, "xessVKGetRequiredDeviceExtensions");
XESS_SYM(a->get_required_device_features, "xessVKGetRequiredDeviceFeatures");
XESS_SYM(a->create_context, "xessVKCreateContext");
XESS_SYM(a->build_pipelines, "xessVKBuildPipelines");
XESS_SYM(a->init, "xessVKInit");
XESS_SYM(a->execute, "xessVKExecute");
return !missing;
}
const xess_lib_t *xess_lib_get(const char *dir)
{
if (!xess_probed)
{
char path[DIR_MAX_LENGTH];
xess_module_t m;
const char *missing = NULL;
bool d3d12, vk;
char version[32];
if (string_is_empty(dir))
{
strlcpy(xess_status, "no XeSS directory", sizeof(xess_status));
return NULL;
}
fill_pathname_join_special(path, dir, XESS_LIBRARY, sizeof(path));
if (!path_is_valid(path) || !(m = xess_open_file(path)))
{
/* (not probed: the directory is a setting, tried again as set) */
strlcpy(xess_status, "no XeSS library (" XESS_LIBRARY ") in \"", sizeof(xess_status));
strlcat(xess_status, dir, sizeof(xess_status));
strlcat(xess_status, "\"", sizeof(xess_status));
return NULL;
}
xess_probed = true;
xess_resolve_common(m, &xess_lib.common, &missing);
d3d12 = xess_resolve_d3d12(m, &xess_lib.d3d12);
vk = xess_resolve_vk(m, &xess_lib.vk);
strlcpy(xess_status, "XeSS library ", sizeof(xess_status));
strlcat(xess_status, path, sizeof(xess_status));
if (missing || !(d3d12 || vk))
{
strlcat(xess_status, missing ? " lacks " : ": neither Direct3D 12 nor Vulkan", sizeof(xess_status));
if (missing)
strlcat(xess_status, missing, sizeof(xess_status));
memset(&xess_lib, 0, sizeof(xess_lib));
return NULL;
}
if (!d3d12)
memset(&xess_lib.d3d12, 0, sizeof(xess_lib.d3d12));
if (!vk)
memset(&xess_lib.vk, 0, sizeof(xess_lib.vk));
if (xess_lib.common.get_version(&xess_lib.version) == XESS_RESULT_SUCCESS)
{
/* (three 16-bit numbers: fits) */
sprintf(version, " %u.%u.%u",
(unsigned)xess_lib.version.major, (unsigned)xess_lib.version.minor,
(unsigned)xess_lib.version.patch);
strlcat(xess_status, version, sizeof(xess_status));
}
strlcat(xess_status, d3d12 && vk ? ": Vulkan and Direct3D 12"
: vk ? ": Vulkan only" : ": Direct3D 12 only", sizeof(xess_status));
xess_lib.handle = (void*)m;
}
return xess_lib.handle ? &xess_lib : NULL;
}
const char *xess_lib_status(void)
{
return xess_status;
}
#else
const xess_lib_t *xess_lib_get(const char *dir)
{
(void)dir;
return NULL;
}
const char *xess_lib_status(void)
{
return "no XeSS on this platform";
}
#endif
+322
View File
@@ -0,0 +1,322 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (ffx_loader.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_GFX_AMD_FFX_LOADER_H
#define __LIBRETRO_SDK_GFX_AMD_FFX_LOADER_H
/* AMD FidelityFX Super Resolution through the FidelityFX API (ffx-api),
* loaded at run time from the library the user supplies: nothing of
* AMD's is built in or shipped. The library is the SDK's own, which
* ships under the MIT license:
* - Direct3D 12: amd_fidelityfx_loader_dx12.dll of SDK 2 (which loads
* amd_fidelityfx_upscaler_dx12.dll beside it: FSR 4 on the GPUs that
* have it, FSR 3.1 elsewhere), or amd_fidelityfx_dx12.dll of SDK 1.1
* (FSR 3.1);
* - Vulkan: amd_fidelityfx_vk.dll of SDK 1.1 (FSR 3.1; SDK 2 has no
* Vulkan).
* One entry point set serves both (ffx_api.h): five functions and
* descriptors chained by their headers, whose layouts and type numbers
* are the SDK's, declared here so that no SDK header is needed. The
* structures mirror the SDK's to the byte on x86-64. */
#include <stddef.h>
#include <stdint.h>
#include <boolean.h>
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
typedef uint32_t ffx_return_t;
#define FFX_RETURN_OK 0
#define FFX_RETURN_ERROR 1
#define FFX_RETURN_ERROR_UNKNOWN_DESCTYPE 2
#define FFX_RETURN_ERROR_RUNTIME_ERROR 3
#define FFX_RETURN_NO_PROVIDER 4
#define FFX_RETURN_ERROR_MEMORY 5
#define FFX_RETURN_ERROR_PARAMETER 6
typedef void *ffx_context_t;
/* Every descriptor starts with this; the API is handed the header */
typedef struct ffx_header
{
uint64_t type;
struct ffx_header *next;
} ffx_header_t;
/* Descriptor types (the effect's id in the high word) */
#define FFX_TYPE_GET_VERSIONS 4u
#define FFX_TYPE_OVERRIDE_VERSION 5u
#define FFX_TYPE_CONFIGURE_GLOBALDEBUG1 1u
#define FFX_TYPE_BACKEND_DX12 0x00000002u
#define FFX_TYPE_BACKEND_VK 0x00000003u
#define FFX_TYPE_UPSCALE_CREATE 0x00010000u
#define FFX_TYPE_UPSCALE_DISPATCH 0x00010001u
#define FFX_TYPE_UPSCALE_RATIO_OF_MODE 0x00010002u
#define FFX_TYPE_UPSCALE_RENDER_OF_MODE 0x00010003u
#define FFX_TYPE_UPSCALE_VERSION 0x0001000bu
/* FfxApiUpscaleQualityMode */
#define FFX_UPSCALE_MODE_NATIVEAA 0
#define FFX_UPSCALE_MODE_QUALITY 1
#define FFX_UPSCALE_MODE_BALANCED 2
#define FFX_UPSCALE_MODE_PERFORMANCE 3
#define FFX_UPSCALE_MODE_ULTRA_PERFORMANCE 4
/* FfxApiCreateContextUpscaleFlags */
#define FFX_UPSCALE_ENABLE_HIGH_DYNAMIC_RANGE (1 << 0)
#define FFX_UPSCALE_ENABLE_DISPLAY_RESOLUTION_MVS (1 << 1)
#define FFX_UPSCALE_ENABLE_MV_JITTER_CANCELLATION (1 << 2)
#define FFX_UPSCALE_ENABLE_DEPTH_INVERTED (1 << 3)
#define FFX_UPSCALE_ENABLE_DEPTH_INFINITE (1 << 4)
#define FFX_UPSCALE_ENABLE_AUTO_EXPOSURE (1 << 5)
#define FFX_UPSCALE_ENABLE_DYNAMIC_RESOLUTION (1 << 6)
#define FFX_UPSCALE_ENABLE_DEBUG_CHECKING (1 << 7)
#define FFX_UPSCALE_ENABLE_NON_LINEAR_COLORSPACE (1 << 8)
/* FfxApiDispatchFsrUpscaleFlags */
#define FFX_UPSCALE_FLAG_NON_LINEAR_COLOR_SRGB (1 << 1)
#define FFX_UPSCALE_FLAG_NON_LINEAR_COLOR_PQ (1 << 2)
/* FfxApiSurfaceFormat, the ones a frame brings */
#define FFX_FORMAT_UNKNOWN 0
#define FFX_FORMAT_R32G32B32A32_FLOAT 3
#define FFX_FORMAT_R16G16B16A16_FLOAT 4
#define FFX_FORMAT_R32G32_FLOAT 6
#define FFX_FORMAT_R8_UINT 7
#define FFX_FORMAT_R32_UINT 8
#define FFX_FORMAT_R8G8B8A8_UNORM 10
#define FFX_FORMAT_R8G8B8A8_SRGB 12
#define FFX_FORMAT_B8G8R8A8_UNORM 14
#define FFX_FORMAT_B8G8R8A8_SRGB 15
#define FFX_FORMAT_R11G11B10_FLOAT 16
#define FFX_FORMAT_R10G10B10A2_UNORM 17
#define FFX_FORMAT_R16G16_FLOAT 18
#define FFX_FORMAT_R16_FLOAT 21
#define FFX_FORMAT_R16_UNORM 23
#define FFX_FORMAT_R8_UNORM 25
#define FFX_FORMAT_R32_FLOAT 28
/* FfxApiResourceUsage */
#define FFX_USAGE_READ_ONLY 0
#define FFX_USAGE_RENDERTARGET (1 << 0)
#define FFX_USAGE_UAV (1 << 1)
#define FFX_USAGE_DEPTHTARGET (1 << 2)
#define FFX_USAGE_STENCILTARGET (1 << 5)
/* FfxApiResourceState */
#define FFX_STATE_COMMON (1 << 0)
#define FFX_STATE_UNORDERED_ACCESS (1 << 1)
#define FFX_STATE_COMPUTE_READ (1 << 2)
#define FFX_STATE_PIXEL_READ (1 << 3)
#define FFX_STATE_COPY_SRC (1 << 4)
#define FFX_STATE_COPY_DEST (1 << 5)
#define FFX_STATE_RENDER_TARGET (1 << 8)
#define FFX_STATE_DEPTH_ATTACHMENT (1 << 9)
/* FfxApiResourceType */
#define FFX_RESOURCE_TEXTURE2D 2
typedef struct ffx_dims
{
uint32_t width;
uint32_t height;
} ffx_dims_t;
typedef struct ffx_coords
{
float x;
float y;
} ffx_coords_t;
/* FfxApiResourceDescription (its unions laid out as their first member) */
typedef struct ffx_resource_desc
{
uint32_t type; /* FFX_RESOURCE_ */
uint32_t format; /* FFX_FORMAT_ */
uint32_t width;
uint32_t height;
uint32_t depth;
uint32_t mip_count;
uint32_t flags;
uint32_t usage; /* FFX_USAGE_ */
} ffx_resource_desc_t;
/* FfxApiResource: the API's handle (ID3D12Resource *, or the VkImage
* as a pointer), and the state it is in, which the dispatch leaves it in */
typedef struct ffx_resource
{
void *resource;
ffx_resource_desc_t description;
uint32_t state; /* FFX_STATE_ */
} ffx_resource_t;
/* A message from the runtime: type 0 error, 1 warning */
typedef void (*ffx_message_t)(uint32_t type, const wchar_t *message);
/* ffxConfigureDescGlobalDebug1, with a NULL context */
typedef struct ffx_configure_debug
{
ffx_header_t header;
ffx_message_t message;
uint32_t debug_level; /* 0 silence, 1 errors, 2 warnings */
} ffx_configure_debug_t;
/* ffxQueryDescGetVersions, with a NULL context: count first (ids and
* names NULL), then both arrays of that many */
typedef struct ffx_query_versions
{
ffx_header_t header;
uint64_t create_desc_type; /* FFX_TYPE_UPSCALE_CREATE */
void *device; /* Direct3D 12: the ID3D12Device *; Vulkan: NULL */
uint64_t *output_count;
uint64_t *version_ids;
const char **version_names;
} ffx_query_versions_t;
/* ffxOverrideVersion: chained after the backend's descriptor */
typedef struct ffx_override_version
{
ffx_header_t header;
uint64_t version_id;
} ffx_override_version_t;
/* ffxCreateBackendDX12Desc */
typedef struct ffx_backend_dx12
{
ffx_header_t header;
void *device; /* ID3D12Device * */
} ffx_backend_dx12_t;
/* ffxCreateBackendVKDesc */
typedef struct ffx_backend_vk
{
ffx_header_t header;
void *device; /* VkDevice */
void *physical_device; /* VkPhysicalDevice */
void *get_device_proc_addr; /* PFN_vkGetDeviceProcAddr */
} ffx_backend_vk_t;
/* ffxCreateContextDescUpscale */
typedef struct ffx_upscale_create
{
ffx_header_t header;
uint32_t flags; /* FFX_UPSCALE_ENABLE_ */
ffx_dims_t max_render_size;
ffx_dims_t max_upscale_size;
ffx_message_t message;
} ffx_upscale_create_t;
/* ffxCreateContextDescUpscaleVersion: the API version built against */
typedef struct ffx_upscale_version
{
ffx_header_t header;
uint32_t version;
} ffx_upscale_version_t;
#define FFX_UPSCALER_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch))
/* ffxDispatchDescUpscale (its bools a byte each, as the SDK's compilers
* have them) */
typedef struct ffx_upscale_dispatch
{
ffx_header_t header;
void *command_list; /* ID3D12GraphicsCommandList *, or VkCommandBuffer */
ffx_resource_t color;
ffx_resource_t depth;
ffx_resource_t motion_vectors;
ffx_resource_t exposure;
ffx_resource_t reactive;
ffx_resource_t transparency_and_composition;
ffx_resource_t output;
ffx_coords_t jitter_offset;
ffx_coords_t motion_vector_scale;
ffx_dims_t render_size;
ffx_dims_t upscale_size;
unsigned char enable_sharpening;
float sharpness;
float frame_time_delta; /* ms */
float pre_exposure;
unsigned char reset;
float camera_near;
float camera_far;
float camera_fov_angle_vertical; /* radians */
float view_space_to_meters_factor;
uint32_t flags; /* FFX_UPSCALE_FLAG_ */
} ffx_upscale_dispatch_t;
/* ffxQueryDescUpscaleGetRenderResolutionFromQualityMode, with a NULL
* context (or one) */
typedef struct ffx_query_render_of_mode
{
ffx_header_t header;
uint32_t display_width;
uint32_t display_height;
uint32_t quality_mode; /* FFX_UPSCALE_MODE_ */
uint32_t *out_render_width;
uint32_t *out_render_height;
} ffx_query_render_of_mode_t;
/* The five entry points of ffx_api.h */
typedef struct ffx_lib
{
void *handle;
ffx_return_t (*create_context)(ffx_context_t *context,
ffx_header_t *desc, const void *mem_cb);
ffx_return_t (*destroy_context)(ffx_context_t *context, const void *mem_cb);
ffx_return_t (*configure)(ffx_context_t *context, const ffx_header_t *desc);
ffx_return_t (*query)(ffx_context_t *context, ffx_header_t *desc);
ffx_return_t (*dispatch)(ffx_context_t *context, const ffx_header_t *desc);
} ffx_lib_t;
/* Which API's library */
enum ffx_api
{
FFX_API_D3D12 = 0,
FFX_API_VULKAN,
FFX_API_LAST
};
/* The library for @api in @dir, loaded the first time it is found and
* kept: NULL where there is none (looked for again next call, as the
* directory is a setting), or one that lacks an entry point. A library
* loaded from one directory is the one there is after; another
* directory takes a restart. */
const ffx_lib_t *ffx_lib_get(enum ffx_api api, const char *dir);
/* Whether @dir holds the library for @api, the file the user supplies.
* Loads nothing. */
bool ffx_runtime_in(enum ffx_api api, const char *dir);
/* What the probe found, for a log: the file loaded, or why not. Valid
* after ffx_lib_get(); before, "not probed". */
const char *ffx_lib_status(enum ffx_api api);
/* The upscaler versions @lib offers (@device: Direct3D 12's, NULL for
* Vulkan), their ids into @ids and names into @names, up to @cap: how
* many there are (0: none, or the query failed). Names are copied
* out by the caller before the next query. The first is the newest. */
unsigned ffx_lib_versions(const ffx_lib_t *lib, void *device,
uint64_t *ids, const char **names, unsigned cap);
RETRO_END_DECLS
#endif
+276
View File
@@ -0,0 +1,276 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (xess_loader.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_GFX_INTEL_XESS_LOADER_H
#define __LIBRETRO_SDK_GFX_INTEL_XESS_LOADER_H
/* Intel XeSS Super Resolution (XeSS-SR), loaded at run time from the
* library the user supplies, libxess.dll of the XeSS SDK (Direct3D 12
* and Vulkan in the one library): nothing of Intel's is built in or
* shipped. The entry points of xess.h, xess_d3d12.h and xess_vk.h, with
* their structures declared here so that no SDK header is needed; the
* SDK packs them to 8, which is their natural layout on x86-64. */
#include <stddef.h>
#include <stdint.h>
#include <boolean.h>
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
typedef struct xess_context *xess_context_t;
typedef int xess_result_t;
#define XESS_RESULT_WARNING_OLD_DRIVER 2
#define XESS_RESULT_SUCCESS 0
#define XESS_RESULT_ERROR_UNSUPPORTED_DEVICE -1
#define XESS_RESULT_ERROR_UNSUPPORTED_DRIVER -2
#define XESS_RESULT_ERROR_UNINITIALIZED -3
#define XESS_RESULT_ERROR_INVALID_ARGUMENT -4
#define XESS_RESULT_ERROR_DEVICE_OUT_OF_MEMORY -5
#define XESS_RESULT_ERROR_DEVICE -6
#define XESS_RESULT_ERROR_NOT_IMPLEMENTED -7
#define XESS_RESULT_ERROR_INVALID_CONTEXT -8
#define XESS_RESULT_ERROR_OPERATION_IN_PROGRESS -9
#define XESS_RESULT_ERROR_UNSUPPORTED -10
#define XESS_RESULT_ERROR_CANT_LOAD_LIBRARY -11
#define XESS_RESULT_ERROR_WRONG_CALL_ORDER -12
#define XESS_FAILED(r) ((r) < 0)
typedef struct xess_version
{
uint16_t major;
uint16_t minor;
uint16_t patch;
uint16_t reserved;
} xess_version_t;
typedef struct xess_2d
{
uint32_t x;
uint32_t y;
} xess_2d_t;
/* xess_quality_settings_t */
#define XESS_QUALITY_ULTRA_PERFORMANCE 100
#define XESS_QUALITY_PERFORMANCE 101
#define XESS_QUALITY_BALANCED 102
#define XESS_QUALITY_QUALITY 103
#define XESS_QUALITY_ULTRA_QUALITY 104
#define XESS_QUALITY_ULTRA_QUALITY_PLUS 105
#define XESS_QUALITY_AA 106
/* xess_init_flags_t */
#define XESS_INIT_FLAG_HIGH_RES_MV (1 << 0)
#define XESS_INIT_FLAG_INVERTED_DEPTH (1 << 1)
#define XESS_INIT_FLAG_EXPOSURE_SCALE_TEXTURE (1 << 2)
#define XESS_INIT_FLAG_RESPONSIVE_PIXEL_MASK (1 << 3)
#define XESS_INIT_FLAG_USE_NDC_VELOCITY (1 << 4)
#define XESS_INIT_FLAG_EXTERNAL_DESCRIPTOR_HEAP (1 << 5)
#define XESS_INIT_FLAG_LDR_INPUT_COLOR (1 << 6)
#define XESS_INIT_FLAG_JITTERED_MV (1 << 7)
#define XESS_INIT_FLAG_ENABLE_AUTOEXPOSURE (1 << 8)
/* xess_logging_level_t */
#define XESS_LOGGING_LEVEL_DEBUG 0
#define XESS_LOGGING_LEVEL_INFO 1
#define XESS_LOGGING_LEVEL_WARNING 2
#define XESS_LOGGING_LEVEL_ERROR 3
typedef void (*xess_log_t)(const char *message, int level);
/* xess_d3d12_init_params_t */
typedef struct xess_d3d12_init_params
{
xess_2d_t output_resolution;
int quality; /* XESS_QUALITY_ */
uint32_t init_flags; /* XESS_INIT_FLAG_ */
uint32_t creation_node_mask;
uint32_t visible_node_mask;
void *temp_buffer_heap; /* ID3D12Heap *, or NULL for the library's own */
uint64_t buffer_heap_offset;
void *temp_texture_heap;
uint64_t texture_heap_offset;
void *pipeline_library; /* ID3D12PipelineLibrary *, or NULL */
} xess_d3d12_init_params_t;
/* xess_d3d12_execute_params_t: the inputs in NON_PIXEL_SHADER_RESOURCE,
* the output in UNORDERED_ACCESS, left there */
typedef struct xess_d3d12_execute_params
{
void *color; /* ID3D12Resource * each */
void *velocity;
void *depth;
void *exposure_scale;
void *responsive_pixel_mask;
void *output;
float jitter_offset_x;
float jitter_offset_y;
float exposure_scale_value;
uint32_t reset_history;
uint32_t input_width;
uint32_t input_height;
xess_2d_t input_color_base;
xess_2d_t input_motion_vector_base;
xess_2d_t input_depth_base;
xess_2d_t input_responsive_mask_base;
xess_2d_t reserved0;
xess_2d_t output_color_base;
void *descriptor_heap; /* ID3D12DescriptorHeap *, or NULL */
uint32_t descriptor_heap_offset;
} xess_d3d12_execute_params_t;
/* xess_vk_image_view_info (the non-dispatchable handles as the 64-bit
* ABI has them) */
typedef struct xess_vk_image_view_info
{
uint64_t image_view; /* VkImageView */
uint64_t image; /* VkImage */
struct
{
uint32_t aspect_mask;
uint32_t base_mip_level;
uint32_t level_count;
uint32_t base_array_layer;
uint32_t layer_count;
} subresource_range;
int format; /* VkFormat */
uint32_t width;
uint32_t height;
} xess_vk_image_view_info_t;
/* xess_vk_execute_params_t: the inputs in SHADER_READ_ONLY_OPTIMAL,
* the output in GENERAL, left there */
typedef struct xess_vk_execute_params
{
xess_vk_image_view_info_t color;
xess_vk_image_view_info_t velocity;
xess_vk_image_view_info_t depth;
xess_vk_image_view_info_t exposure_scale;
xess_vk_image_view_info_t responsive_pixel_mask;
xess_vk_image_view_info_t output;
float jitter_offset_x;
float jitter_offset_y;
float exposure_scale_value;
uint32_t reset_history;
uint32_t input_width;
uint32_t input_height;
xess_2d_t input_color_base;
xess_2d_t input_motion_vector_base;
xess_2d_t input_depth_base;
xess_2d_t input_responsive_mask_base;
xess_2d_t reserved0;
xess_2d_t output_color_base;
} xess_vk_execute_params_t;
/* xess_vk_init_params_t */
typedef struct xess_vk_init_params
{
xess_2d_t output_resolution;
int quality;
uint32_t init_flags;
uint32_t creation_node_mask;
uint32_t visible_node_mask;
uint64_t temp_buffer_heap; /* VkDeviceMemory, or 0 for the library's own */
uint64_t buffer_heap_offset;
uint64_t temp_texture_heap;
uint64_t texture_heap_offset;
uint64_t pipeline_cache; /* VkPipelineCache, or 0 */
} xess_vk_init_params_t;
/* The entry points of xess.h the frontend uses */
typedef struct xess_common_api
{
xess_result_t (*get_version)(xess_version_t *version);
xess_result_t (*get_optimal_input_resolution)(xess_context_t ctx,
const xess_2d_t *output, int quality, xess_2d_t *optimal,
xess_2d_t *min, xess_2d_t *max);
xess_result_t (*destroy_context)(xess_context_t ctx);
xess_result_t (*set_jitter_scale)(xess_context_t ctx, float x, float y);
xess_result_t (*set_velocity_scale)(xess_context_t ctx, float x, float y);
xess_result_t (*set_exposure_multiplier)(xess_context_t ctx, float scale);
xess_result_t (*set_logging_callback)(xess_context_t ctx, int level,
xess_log_t callback);
/* After a non-blocking build_pipelines, before init: SUCCESS once
* built, XESS_RESULT_ERROR_OPERATION_IN_PROGRESS while building.
* NULL in a library without it: init builds them, blocking. */
xess_result_t (*get_pipeline_build_status)(xess_context_t ctx);
} xess_common_api_t;
/* xess_d3d12.h; device, command list and pipeline library are Direct3D's */
typedef struct xess_d3d12_api
{
xess_result_t (*create_context)(void *device, xess_context_t *ctx);
xess_result_t (*build_pipelines)(xess_context_t ctx,
void *pipeline_library, bool blocking, uint32_t init_flags);
xess_result_t (*init)(xess_context_t ctx, const xess_d3d12_init_params_t *params);
xess_result_t (*execute)(xess_context_t ctx, void *command_list,
const xess_d3d12_execute_params_t *params);
} xess_d3d12_api_t;
/* xess_vk.h; instance, physical device, device and command buffer are
* Vulkan's dispatchable handles */
typedef struct xess_vk_api
{
xess_result_t (*get_required_instance_extensions)(uint32_t *count,
const char *const **extensions, uint32_t *min_api_version);
xess_result_t (*get_required_device_extensions)(void *instance,
void *physical_device, uint32_t *count, const char *const **extensions);
/* a chain of VkPhysicalDevice*Features structures to put on the
* device's pNext, the library's own */
xess_result_t (*get_required_device_features)(void *instance,
void *physical_device, void **features);
xess_result_t (*create_context)(void *instance, void *physical_device,
void *device, xess_context_t *ctx);
xess_result_t (*build_pipelines)(xess_context_t ctx,
uint64_t pipeline_cache, bool blocking, uint32_t init_flags);
xess_result_t (*init)(xess_context_t ctx, const xess_vk_init_params_t *params);
xess_result_t (*execute)(xess_context_t ctx, void *command_buffer,
const xess_vk_execute_params_t *params);
} xess_vk_api_t;
typedef struct xess_lib
{
void *handle;
xess_common_api_t common;
xess_d3d12_api_t d3d12; /* d3d12.create_context NULL: no Direct3D 12 */
xess_vk_api_t vk; /* vk.create_context NULL: no Vulkan */
xess_version_t version;
} xess_lib_t;
/* The library in @dir (libxess.dll; libxess.so elsewhere), loaded the
* first time it is found and kept: NULL where there is none (looked for
* again next call, as the directory is a setting), or one that lacks
* the common entry points or both APIs. Another directory takes a
* restart. */
const xess_lib_t *xess_lib_get(const char *dir);
/* Whether @dir holds the library, the file the user supplies. Loads
* nothing. */
bool xess_runtime_in(const char *dir);
/* What the probe found, for a log. Valid after xess_lib_get(); before,
* "not probed". */
const char *xess_lib_status(void);
RETRO_END_DECLS
#endif
+77
View File
@@ -0,0 +1,77 @@
include ../../sample_objdir.mk
TARGET := ffx_loader_test
LIBRETRO_COMM_DIR := ../../..
# The FidelityFX API loader against a stand-in library (fake_ffx.c),
# planted by the test under the SDK's file names in a directory of its
# own, as the user's runtime directory is. The loader is C89 everywhere
# it ships and is held to it here.
SOURCES := \
ffx_loader_test.c \
$(LIBRETRO_COMM_DIR)/gfx/amd/ffx_loader.c \
$(LIBRETRO_COMM_DIR)/compat/fopen_utf8.c \
$(LIBRETRO_COMM_DIR)/compat/compat_strl.c \
$(LIBRETRO_COMM_DIR)/compat/compat_strcasestr.c \
$(LIBRETRO_COMM_DIR)/compat/compat_posix_string.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_utf.c \
$(LIBRETRO_COMM_DIR)/file/file_path.c \
$(LIBRETRO_COMM_DIR)/file/file_path_io.c \
$(LIBRETRO_COMM_DIR)/file/retro_dirent.c \
$(LIBRETRO_COMM_DIR)/lists/string_list.c \
$(LIBRETRO_COMM_DIR)/streams/file_stream.c \
$(LIBRETRO_COMM_DIR)/string/stdstring.c \
$(LIBRETRO_COMM_DIR)/string/rstrtod.c \
$(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c \
$(LIBRETRO_COMM_DIR)/time/rtime.c
OBJS := $(call sample_objs,$(SOURCES:.c=.o))
ifneq ($(findstring mingw,$(CC)),)
EXE := .exe
FAKE := fake_ffx.dll
LDLIBS :=
RUNNER ?= wine
else
EXE :=
FAKE := libfake_ffx.so
LDLIBS := -ldl
RUNNER ?=
endif
CFLAGS += -Wall -pedantic -std=c89 -Wno-long-long -Wno-variadic-macros -D_GNU_SOURCE -g1 \
-Werror=implicit-function-declaration \
-I$(LIBRETRO_COMM_DIR)/include
ifneq ($(SANITIZER),)
CFLAGS := -fsanitize=$(SANITIZER) -fno-omit-frame-pointer $(CFLAGS)
LDFLAGS := -fsanitize=$(SANITIZER) $(LDFLAGS)
endif
all: $(TARGET)$(EXE) $(FAKE)
fake_ffx.dll: fake_ffx.c fake_ffx.h
$(CC) -shared -O1 -Wall -o $@ $<
libfake_ffx.so: fake_ffx.c fake_ffx.h
$(CC) -shared -fPIC -O1 -Wall -o $@ $<
%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
$(SAMPLE_OBJDIR)/%.o: $$(call sample_src,$$*).c
@mkdir -p $(@D)
$(CC) -c -o $@ $< $(CFLAGS)
$(TARGET)$(EXE): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS)
check: all
$(RUNNER) ./$(TARGET)$(EXE)
clean:
rm -f $(TARGET) $(TARGET).exe $(TARGET).o fake_ffx.dll libfake_ffx.so
rm -rf $(SAMPLE_OBJDIR) ffx_loader_test_dir
.PHONY: all check clean
+233
View File
@@ -0,0 +1,233 @@
/* A stand-in FidelityFX API library (amd_fidelityfx_dx12.dll and
* amd_fidelityfx_vk.dll on Windows, their .so names on Linux): exports
* the five ffx-api entry points gfx/amd/ffx_loader.c resolves, laid out
* from the SDK's own descriptor layouts (declared here again, as the
* SDK's C++ headers have them, with no dependence on the loader's),
* and records what it was asked (fake_ffx.h). Two upscaler versions, a
* context a handle, the render size of a mode by the SDK's ratios. */
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "fake_ffx.h"
#if defined(_WIN32)
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __attribute__((visibility("default")))
#endif
/* The SDK's layouts (ffx_api.h, ffx_api_types.h, ffx_upscale.h) */
typedef struct sdk_header { uint64_t type; struct sdk_header *pNext; } sdk_header;
struct sdk_dims { uint32_t width, height; };
struct sdk_coords { float x, y; };
struct sdk_resource_desc { uint32_t type, format, width, height, depth, mipCount, flags, usage; };
struct sdk_resource { void *resource; struct sdk_resource_desc description; uint32_t state; };
struct sdk_create_upscale { sdk_header header; uint32_t flags; struct sdk_dims maxRenderSize, maxUpscaleSize; void (*fpMessage)(uint32_t, const wchar_t*); };
struct sdk_dispatch_upscale
{
sdk_header header; void *commandList;
struct sdk_resource color, depth, motionVectors, exposure, reactive, transparencyAndComposition, output;
struct sdk_coords jitterOffset, motionVectorScale;
struct sdk_dims renderSize, upscaleSize;
unsigned char enableSharpening; float sharpness, frameTimeDelta, preExposure;
unsigned char reset; float cameraNear, cameraFar, cameraFovAngleVertical, viewSpaceToMetersFactor;
uint32_t flags;
};
struct sdk_backend_dx12 { sdk_header header; void *device; };
struct sdk_backend_vk { sdk_header header; void *vkDevice, *vkPhysicalDevice, *vkDeviceProcAddr; };
struct sdk_override_version { sdk_header header; uint64_t versionId; };
struct sdk_upscale_version { sdk_header header; uint32_t version; };
struct sdk_get_versions { sdk_header header; uint64_t createDescType; void *device; uint64_t *outputCount; uint64_t *versionIds; const char **versionNames; };
struct sdk_render_of_mode { sdk_header header; uint32_t displayWidth, displayHeight, qualityMode; uint32_t *pOutRenderWidth, *pOutRenderHeight; };
struct sdk_debug1 { sdk_header header; void (*fpMessage)(uint32_t, const wchar_t*); uint32_t debugLevel; };
#define T_GET_VERSIONS 4u
#define T_OVERRIDE_VERSION 5u
#define T_DEBUG1 1u
#define T_BACKEND_DX12 2u
#define T_BACKEND_VK 3u
#define T_UPSCALE_CREATE 0x00010000u
#define T_UPSCALE_DISPATCH 0x00010001u
#define T_RENDER_OF_MODE 0x00010003u
#define T_UPSCALE_VERSION 0x0001000bu
#define OK 0u
#define ERR 1u
#define ERR_PARAMETER 6u
static fake_ffx_record_t record;
static int contexts[FAKE_FFX_MAX_CALLS];
static const uint64_t version_ids[2] = { 0x4002, 0x3104 };
static const char *version_names[2] = { "FSR 4.0.2", "FSR 3.1.4" };
EXPORT fake_ffx_record_t *fake_ffx_record(void) { return &record; }
static void take_resource(fake_ffx_resource_t *r, const struct sdk_resource *s)
{
r->present = s->resource != NULL;
r->resource = s->resource;
r->type = s->description.type;
r->format = s->description.format;
r->width = s->description.width;
r->height = s->description.height;
r->mip_count = s->description.mipCount;
r->usage = s->description.usage;
r->state = s->state;
}
EXPORT uint32_t ffxCreateContext(void **context, sdk_header *desc, const void *memCb)
{
fake_ffx_create_t *c;
const sdk_header *h;
int i;
(void)memCb;
if (!context || !desc || desc->type != T_UPSCALE_CREATE)
return ERR_PARAMETER;
if (record.creates < FAKE_FFX_MAX_CALLS)
{
const struct sdk_create_upscale *u = (const struct sdk_create_upscale*)desc;
c = &record.create[record.creates];
memset(c, 0, sizeof(*c));
c->flags = u->flags;
c->max_render_w = u->maxRenderSize.width;
c->max_render_h = u->maxRenderSize.height;
c->max_upscale_w = u->maxUpscaleSize.width;
c->max_upscale_h = u->maxUpscaleSize.height;
c->has_message = u->fpMessage != NULL;
for (h = desc->pNext; h; h = h->pNext)
{
if (h->type == T_BACKEND_DX12)
{
c->backend_type = h->type;
c->device = ((const struct sdk_backend_dx12*)h)->device;
}
else if (h->type == T_BACKEND_VK)
{
c->backend_type = h->type;
c->device = ((const struct sdk_backend_vk*)h)->vkDevice;
c->physical_device = ((const struct sdk_backend_vk*)h)->vkPhysicalDevice;
c->get_device_proc_addr = ((const struct sdk_backend_vk*)h)->vkDeviceProcAddr;
}
else if (h->type == T_OVERRIDE_VERSION)
c->version_id = ((const struct sdk_override_version*)h)->versionId;
else if (h->type == T_UPSCALE_VERSION)
c->api_version = ((const struct sdk_upscale_version*)h)->version;
}
}
record.creates++;
if (record.create_fails)
return ERR;
for (i = 0; i < FAKE_FFX_MAX_CALLS; i++)
if (!contexts[i])
{
contexts[i] = 1;
*context = &contexts[i];
return OK;
}
return ERR;
}
EXPORT uint32_t ffxDestroyContext(void **context, const void *memCb)
{
(void)memCb;
if (!context || !*context)
return ERR_PARAMETER;
*(int*)*context = 0;
record.last_destroyed = *context;
record.destroys++;
*context = NULL;
return OK;
}
EXPORT uint32_t ffxConfigure(void **context, const sdk_header *desc)
{
(void)context;
record.configures++;
if (desc && desc->type == T_DEBUG1)
{
record.debug_level = ((const struct sdk_debug1*)desc)->debugLevel;
return OK;
}
return ERR_PARAMETER;
}
EXPORT uint32_t ffxQuery(void **context, sdk_header *desc)
{
record.queries++;
if (!desc)
return ERR_PARAMETER;
if (desc->type == T_GET_VERSIONS)
{
struct sdk_get_versions *q = (struct sdk_get_versions*)desc;
uint64_t n = record.no_versions ? 0 : 2, i;
if (record.versions_need_device && !q->device)
n = 0;
if (q->createDescType != T_UPSCALE_CREATE || !q->outputCount)
return ERR_PARAMETER;
if (q->versionIds || q->versionNames)
{
if (*q->outputCount < n)
n = *q->outputCount;
for (i = 0; i < n; i++)
{
if (q->versionIds)
q->versionIds[i] = version_ids[i];
if (q->versionNames)
q->versionNames[i] = version_names[i];
}
}
*q->outputCount = n;
return OK;
}
if (desc->type == T_RENDER_OF_MODE)
{
struct sdk_render_of_mode *q = (struct sdk_render_of_mode*)desc;
static const float ratios[5] = { 1.0f, 1.5f, 1.7f, 2.0f, 3.0f };
(void)context;
if (q->qualityMode > 4 || !q->pOutRenderWidth || !q->pOutRenderHeight)
return ERR_PARAMETER;
*q->pOutRenderWidth = (uint32_t)((float)q->displayWidth / ratios[q->qualityMode]);
*q->pOutRenderHeight = (uint32_t)((float)q->displayHeight / ratios[q->qualityMode]);
return OK;
}
return ERR;
}
EXPORT uint32_t ffxDispatch(void **context, const sdk_header *desc)
{
if (!context || !*context || !desc || desc->type != T_UPSCALE_DISPATCH)
return ERR_PARAMETER;
if (record.dispatches < FAKE_FFX_MAX_CALLS)
{
const struct sdk_dispatch_upscale *u = (const struct sdk_dispatch_upscale*)desc;
fake_ffx_dispatch_t *d = &record.dispatch[record.dispatches];
memset(d, 0, sizeof(*d));
d->command_list = u->commandList;
take_resource(&d->color, &u->color);
take_resource(&d->depth, &u->depth);
take_resource(&d->motion_vectors, &u->motionVectors);
take_resource(&d->exposure, &u->exposure);
take_resource(&d->reactive, &u->reactive);
take_resource(&d->transparency, &u->transparencyAndComposition);
take_resource(&d->output, &u->output);
d->jitter_x = u->jitterOffset.x;
d->jitter_y = u->jitterOffset.y;
d->mv_scale_x = u->motionVectorScale.x;
d->mv_scale_y = u->motionVectorScale.y;
d->render_w = u->renderSize.width;
d->render_h = u->renderSize.height;
d->upscale_w = u->upscaleSize.width;
d->upscale_h = u->upscaleSize.height;
d->sharpening = u->enableSharpening;
d->reset = u->reset;
d->frame_time = u->frameTimeDelta;
d->pre_exposure = u->preExposure;
d->camera_near = u->cameraNear;
d->camera_far = u->cameraFar;
d->fov = u->cameraFovAngleVertical;
d->flags = u->flags;
}
record.dispatches++;
return record.dispatch_fails ? ERR : OK;
}
+55
View File
@@ -0,0 +1,55 @@
/* What the stand-in FidelityFX API library (fake_ffx.c) saw: read by a
* test through its exported fake_ffx_record(). Plain C, no SDK. */
#ifndef FAKE_FFX_H
#define FAKE_FFX_H
#include <stdint.h>
#define FAKE_FFX_MAX_CALLS 8
typedef struct fake_ffx_resource
{
int present;
void *resource;
uint32_t type, format, width, height, mip_count, usage, state;
} fake_ffx_resource_t;
typedef struct fake_ffx_create
{
uint32_t flags;
uint32_t max_render_w, max_render_h, max_upscale_w, max_upscale_h;
int has_message;
uint64_t backend_type; /* 2 D3D12, 3 Vulkan, 0 none */
void *device; /* the backend's device */
void *physical_device; /* Vulkan's */
void *get_device_proc_addr;
uint64_t version_id; /* the override, 0 none */
uint32_t api_version; /* the version descriptor, 0 none */
} fake_ffx_create_t;
typedef struct fake_ffx_dispatch
{
void *command_list;
fake_ffx_resource_t color, depth, motion_vectors, exposure, reactive, transparency, output;
float jitter_x, jitter_y, mv_scale_x, mv_scale_y;
uint32_t render_w, render_h, upscale_w, upscale_h;
int sharpening, reset;
float frame_time, pre_exposure, camera_near, camera_far, fov;
uint32_t flags;
} fake_ffx_dispatch_t;
typedef struct fake_ffx_record
{
unsigned creates, destroys, dispatches, queries, configures;
fake_ffx_create_t create[FAKE_FFX_MAX_CALLS];
fake_ffx_dispatch_t dispatch[FAKE_FFX_MAX_CALLS];
uint32_t debug_level;
/* the test's knobs */
int create_fails;
int dispatch_fails;
int no_versions; /* the version query finds none */
int versions_need_device; /* ...unless a device is given (Direct3D 12's library) */
void *last_destroyed;
} fake_ffx_record_t;
#endif
+284
View File
@@ -0,0 +1,284 @@
/* gfx/amd/ffx_loader.c against a stand-in FidelityFX API library
* (fake_ffx.c) in a directory of the test's: the descriptor layouts
* pinned to the SDK's, the library found by each API's names, every
* entry point resolved, the version query, a context made from a
* chain the library reads as the SDK does, a dispatch's descriptor
* read back whole. C89. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gfx/amd/ffx_loader.h>
#include <file/file_path.h>
#if defined(_WIN32)
#include <windows.h>
#else
#include <dlfcn.h>
#endif
#include "fake_ffx.h"
/* The SDK's sizes and offsets on x86-64 (printed from its headers) */
#define LAYOUT(name, cond) typedef char layout_##name[(cond) ? 1 : -1]
LAYOUT(header, sizeof(ffx_header_t) == 16);
LAYOUT(resource, sizeof(ffx_resource_t) == 48);
LAYOUT(create, sizeof(ffx_upscale_create_t) == 48);
LAYOUT(create_m, offsetof(ffx_upscale_create_t, message) == 40);
LAYOUT(dispatch, sizeof(ffx_upscale_dispatch_t) == 432);
LAYOUT(disp_out, offsetof(ffx_upscale_dispatch_t, output) == 312);
LAYOUT(disp_jit, offsetof(ffx_upscale_dispatch_t, jitter_offset) == 360);
LAYOUT(disp_shp, offsetof(ffx_upscale_dispatch_t, enable_sharpening) == 392);
LAYOUT(disp_sh, offsetof(ffx_upscale_dispatch_t, sharpness) == 396);
LAYOUT(disp_rst, offsetof(ffx_upscale_dispatch_t, reset) == 408);
LAYOUT(disp_nr, offsetof(ffx_upscale_dispatch_t, camera_near) == 412);
LAYOUT(disp_fl, offsetof(ffx_upscale_dispatch_t, flags) == 428);
LAYOUT(versions, sizeof(ffx_query_versions_t) == 56);
LAYOUT(override, sizeof(ffx_override_version_t) == 24);
LAYOUT(dx12, sizeof(ffx_backend_dx12_t) == 24);
LAYOUT(vk, sizeof(ffx_backend_vk_t) == 40);
LAYOUT(render, sizeof(ffx_query_render_of_mode_t) == 48);
LAYOUT(debug, sizeof(ffx_configure_debug_t) == 32);
static int failures;
#define CHECK(cond) \
do { \
if (!(cond)) \
{ \
printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); \
failures++; \
} \
} while (0)
static fake_ffx_record_t *record_of(const ffx_lib_t *lib)
{
fake_ffx_record_t *(*get)(void) = NULL;
#if defined(_WIN32)
FARPROC p = GetProcAddress((HMODULE)lib->handle, "fake_ffx_record");
#else
void *p = dlsym(lib->handle, "fake_ffx_record");
#endif
memcpy(&get, &p, sizeof(get));
return get ? get() : NULL;
}
/* The stand-in copied into @dir under @name (a run's own directory) */
static int plant(const char *dir, const char *name)
{
char path[256];
FILE *in, *out;
char buf[4096];
size_t n;
path_mkdir(dir);
#if defined(_WIN32)
in = fopen("fake_ffx.dll", "rb");
#else
in = fopen("libfake_ffx.so", "rb");
#endif
if (!in)
return 0;
sprintf(path, "%s/%s", dir, name);
if (!(out = fopen(path, "wb")))
{
fclose(in);
return 0;
}
while ((n = fread(buf, 1, sizeof(buf), in)) > 0)
fwrite(buf, 1, n, out);
fclose(in);
fclose(out);
return 1;
}
static void messages(uint32_t type, const wchar_t *message)
{
(void)type;
(void)message;
}
int main(void)
{
const ffx_lib_t *lib;
fake_ffx_record_t *rec;
uint64_t ids[4];
const char *names[4];
ffx_context_t ctx = NULL;
ffx_upscale_create_t create;
ffx_backend_dx12_t backend;
ffx_override_version_t override;
ffx_upscale_dispatch_t dispatch;
ffx_query_render_of_mode_t render;
ffx_configure_debug_t debug;
uint32_t rw = 0, rh = 0;
char path[256];
int fake_device = 0, fake_list = 0, fake_color = 0, fake_out = 0;
#if defined(_WIN32)
const char *d3d12_name = "amd_fidelityfx_dx12.dll";
const char *vk_name = "amd_fidelityfx_vk.dll";
#else
const char *d3d12_name = "libamd_fidelityfx_dx12.so";
const char *vk_name = "libamd_fidelityfx_vk.so";
#endif
/* (a previous run's) */
sprintf(path, "ffx_loader_test_dir/%s", d3d12_name);
remove(path);
sprintf(path, "ffx_loader_test_dir/%s", vk_name);
remove(path);
/* Nothing in an empty or missing directory, and nothing loaded */
CHECK(!ffx_runtime_in(FFX_API_D3D12, ""));
CHECK(!ffx_runtime_in(FFX_API_D3D12, "ffx_loader_test_none"));
CHECK(ffx_lib_get(FFX_API_D3D12, "ffx_loader_test_none") == NULL);
CHECK(strstr(ffx_lib_status(FFX_API_D3D12), "no FSR library") != NULL);
CHECK(strstr(ffx_lib_status(FFX_API_D3D12), "amd_fidelityfx_dx12") != NULL);
CHECK(strstr(ffx_lib_status(FFX_API_D3D12), "ffx_loader_test_none") != NULL);
CHECK(!strcmp(ffx_lib_status(FFX_API_VULKAN), "not probed"));
/* The Vulkan library by its name, each entry point, the versions */
CHECK(plant("ffx_loader_test_dir", vk_name));
CHECK(ffx_runtime_in(FFX_API_VULKAN, "ffx_loader_test_dir"));
CHECK(!ffx_runtime_in(FFX_API_D3D12, "ffx_loader_test_dir"));
lib = ffx_lib_get(FFX_API_VULKAN, "ffx_loader_test_dir");
CHECK(lib != NULL);
if (!lib)
return 1;
CHECK(lib->create_context && lib->destroy_context && lib->configure && lib->query && lib->dispatch);
CHECK(strstr(ffx_lib_status(FFX_API_VULKAN), vk_name) != NULL);
/* (the same library after, whatever directory is asked) */
CHECK(ffx_lib_get(FFX_API_VULKAN, "ffx_loader_test_none") == lib);
rec = record_of(lib);
CHECK(rec != NULL);
if (!rec)
return 1;
CHECK(ffx_lib_versions(lib, NULL, ids, names, 4) == 2);
CHECK(ids[0] == 0x4002 && ids[1] == 0x3104);
CHECK(!strcmp(names[0], "FSR 4.0.2") && !strcmp(names[1], "FSR 3.1.4"));
CHECK(ffx_lib_versions(lib, NULL, ids, names, 1) == 1 && ids[0] == 0x4002);
rec->no_versions = 1;
CHECK(ffx_lib_versions(lib, NULL, ids, names, 4) == 0);
rec->no_versions = 0;
rec->versions_need_device = 1;
CHECK(ffx_lib_versions(lib, NULL, ids, names, 4) == 0);
CHECK(ffx_lib_versions(lib, &fake_device, ids, names, 4) == 2);
rec->versions_need_device = 0;
/* The debug configuration, with no context */
memset(&debug, 0, sizeof(debug));
debug.header.type = FFX_TYPE_CONFIGURE_GLOBALDEBUG1;
debug.message = messages;
debug.debug_level = 2;
CHECK(lib->configure(NULL, &debug.header) == FFX_RETURN_OK && rec->debug_level == 2);
/* The render size of a mode, with no context */
memset(&render, 0, sizeof(render));
render.header.type = FFX_TYPE_UPSCALE_RENDER_OF_MODE;
render.display_width = 1920;
render.display_height = 1080;
render.quality_mode = FFX_UPSCALE_MODE_PERFORMANCE;
render.out_render_width = &rw;
render.out_render_height = &rh;
CHECK(lib->query(NULL, &render.header) == FFX_RETURN_OK && rw == 960 && rh == 540);
/* A context from the chain: the upscaler's descriptor, the backend,
* the version override */
memset(&create, 0, sizeof(create));
memset(&backend, 0, sizeof(backend));
memset(&override, 0, sizeof(override));
create.header.type = FFX_TYPE_UPSCALE_CREATE;
create.header.next = &backend.header;
create.flags = FFX_UPSCALE_ENABLE_DEPTH_INVERTED | FFX_UPSCALE_ENABLE_AUTO_EXPOSURE;
create.max_render_size.width = 1920;
create.max_render_size.height = 1080;
create.max_upscale_size.width = 1920;
create.max_upscale_size.height = 1080;
create.message = messages;
backend.header.type = FFX_TYPE_BACKEND_DX12;
backend.header.next = &override.header;
backend.device = &fake_device;
override.header.type = FFX_TYPE_OVERRIDE_VERSION;
override.version_id = 0x3104;
CHECK(lib->create_context(&ctx, &create.header, NULL) == FFX_RETURN_OK && ctx != NULL);
CHECK(rec->creates == 1);
CHECK(rec->create[0].flags == (FFX_UPSCALE_ENABLE_DEPTH_INVERTED | FFX_UPSCALE_ENABLE_AUTO_EXPOSURE));
CHECK(rec->create[0].max_render_w == 1920 && rec->create[0].max_upscale_h == 1080);
CHECK(rec->create[0].has_message);
CHECK(rec->create[0].backend_type == FFX_TYPE_BACKEND_DX12 && rec->create[0].device == &fake_device);
CHECK(rec->create[0].version_id == 0x3104);
/* A dispatch: every field where the library expects it */
memset(&dispatch, 0, sizeof(dispatch));
dispatch.header.type = FFX_TYPE_UPSCALE_DISPATCH;
dispatch.command_list = &fake_list;
dispatch.color.resource = &fake_color;
dispatch.color.description.type = FFX_RESOURCE_TEXTURE2D;
dispatch.color.description.format = FFX_FORMAT_R8G8B8A8_UNORM;
dispatch.color.description.width = 1280;
dispatch.color.description.height = 720;
dispatch.color.description.mip_count = 1;
dispatch.color.state = FFX_STATE_COMPUTE_READ;
dispatch.output.resource = &fake_out;
dispatch.output.description.usage = FFX_USAGE_UAV;
dispatch.output.state = FFX_STATE_UNORDERED_ACCESS;
dispatch.jitter_offset.x = 0.25f;
dispatch.jitter_offset.y = -0.125f;
dispatch.motion_vector_scale.x = 1.0f;
dispatch.motion_vector_scale.y = -1.0f;
dispatch.render_size.width = 1280;
dispatch.render_size.height = 720;
dispatch.upscale_size.width = 1920;
dispatch.upscale_size.height = 1080;
dispatch.enable_sharpening = 0;
dispatch.sharpness = 0.0f;
dispatch.frame_time_delta = 16.6f;
dispatch.pre_exposure = 1.0f;
dispatch.reset = 1;
dispatch.camera_near = 0.1f;
dispatch.camera_far = 1000.0f;
dispatch.camera_fov_angle_vertical = 1.0f;
dispatch.flags = FFX_UPSCALE_FLAG_NON_LINEAR_COLOR_SRGB;
CHECK(lib->dispatch(&ctx, &dispatch.header) == FFX_RETURN_OK);
CHECK(rec->dispatches == 1);
CHECK(rec->dispatch[0].command_list == &fake_list);
CHECK(rec->dispatch[0].color.present && rec->dispatch[0].color.resource == &fake_color
&& rec->dispatch[0].color.format == FFX_FORMAT_R8G8B8A8_UNORM
&& rec->dispatch[0].color.width == 1280 && rec->dispatch[0].color.height == 720
&& rec->dispatch[0].color.mip_count == 1 && rec->dispatch[0].color.state == FFX_STATE_COMPUTE_READ);
CHECK(!rec->dispatch[0].depth.present && !rec->dispatch[0].exposure.present);
CHECK(rec->dispatch[0].output.present && rec->dispatch[0].output.usage == FFX_USAGE_UAV
&& rec->dispatch[0].output.state == FFX_STATE_UNORDERED_ACCESS);
CHECK(rec->dispatch[0].jitter_x == 0.25f && rec->dispatch[0].jitter_y == -0.125f);
CHECK(rec->dispatch[0].mv_scale_x == 1.0f && rec->dispatch[0].mv_scale_y == -1.0f);
CHECK(rec->dispatch[0].render_w == 1280 && rec->dispatch[0].upscale_h == 1080);
CHECK(!rec->dispatch[0].sharpening && rec->dispatch[0].reset == 1);
CHECK(rec->dispatch[0].frame_time == 16.6f && rec->dispatch[0].pre_exposure == 1.0f);
CHECK(rec->dispatch[0].camera_near == 0.1f && rec->dispatch[0].camera_far == 1000.0f
&& rec->dispatch[0].fov == 1.0f);
CHECK(rec->dispatch[0].flags == FFX_UPSCALE_FLAG_NON_LINEAR_COLOR_SRGB);
rec->dispatch_fails = 1;
CHECK(lib->dispatch(&ctx, &dispatch.header) != FFX_RETURN_OK);
rec->dispatch_fails = 0;
{
void *was = ctx;
CHECK(lib->destroy_context(&ctx, NULL) == FFX_RETURN_OK && ctx == NULL);
CHECK(rec->destroys == 1 && rec->last_destroyed == was);
}
rec->create_fails = 1;
CHECK(lib->create_context(&ctx, &create.header, NULL) != FFX_RETURN_OK && ctx == NULL);
rec->create_fails = 0;
/* The Direct3D 12 library, by its SDK 1.1 name */
CHECK(plant("ffx_loader_test_dir", d3d12_name));
CHECK(ffx_runtime_in(FFX_API_D3D12, "ffx_loader_test_dir"));
/* (the earlier miss did not stand: found now) */
CHECK(ffx_lib_get(FFX_API_D3D12, "ffx_loader_test_dir") != NULL);
CHECK(ffx_lib_get(FFX_API_D3D12, "ffx_loader_test_dir") != lib);
CHECK(strstr(ffx_lib_status(FFX_API_D3D12), d3d12_name) != NULL);
if (failures)
{
printf("ffx_loader_test: %d failure(s)\n", failures);
return 1;
}
printf("ffx_loader_test: ok\n");
return 0;
}
+77
View File
@@ -0,0 +1,77 @@
include ../../sample_objdir.mk
TARGET := xess_loader_test
LIBRETRO_COMM_DIR := ../../..
# The XeSS loader against a stand-in library (fake_xess.c), planted by
# the test under the SDK's file name in a directory of its own, as the
# user's runtime directory is. The loader is C89 everywhere it ships
# and is held to it here.
SOURCES := \
xess_loader_test.c \
$(LIBRETRO_COMM_DIR)/gfx/intel/xess_loader.c \
$(LIBRETRO_COMM_DIR)/compat/fopen_utf8.c \
$(LIBRETRO_COMM_DIR)/compat/compat_strl.c \
$(LIBRETRO_COMM_DIR)/compat/compat_strcasestr.c \
$(LIBRETRO_COMM_DIR)/compat/compat_posix_string.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_utf.c \
$(LIBRETRO_COMM_DIR)/file/file_path.c \
$(LIBRETRO_COMM_DIR)/file/file_path_io.c \
$(LIBRETRO_COMM_DIR)/file/retro_dirent.c \
$(LIBRETRO_COMM_DIR)/lists/string_list.c \
$(LIBRETRO_COMM_DIR)/streams/file_stream.c \
$(LIBRETRO_COMM_DIR)/string/stdstring.c \
$(LIBRETRO_COMM_DIR)/string/rstrtod.c \
$(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c \
$(LIBRETRO_COMM_DIR)/time/rtime.c
OBJS := $(call sample_objs,$(SOURCES:.c=.o))
ifneq ($(findstring mingw,$(CC)),)
EXE := .exe
FAKE := fake_xess.dll
LDLIBS :=
RUNNER ?= wine
else
EXE :=
FAKE := libfake_xess.so
LDLIBS := -ldl
RUNNER ?=
endif
CFLAGS += -Wall -pedantic -std=c89 -Wno-long-long -Wno-variadic-macros -D_GNU_SOURCE -g1 \
-Werror=implicit-function-declaration \
-I$(LIBRETRO_COMM_DIR)/include
ifneq ($(SANITIZER),)
CFLAGS := -fsanitize=$(SANITIZER) -fno-omit-frame-pointer $(CFLAGS)
LDFLAGS := -fsanitize=$(SANITIZER) $(LDFLAGS)
endif
all: $(TARGET)$(EXE) $(FAKE)
fake_xess.dll: fake_xess.c fake_xess.h
$(CC) -shared -O1 -Wall -o $@ $<
libfake_xess.so: fake_xess.c fake_xess.h
$(CC) -shared -fPIC -O1 -Wall -o $@ $<
%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
$(SAMPLE_OBJDIR)/%.o: $$(call sample_src,$$*).c
@mkdir -p $(@D)
$(CC) -c -o $@ $< $(CFLAGS)
$(TARGET)$(EXE): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS)
check: all
$(RUNNER) ./$(TARGET)$(EXE)
clean:
rm -f $(TARGET) $(TARGET).exe $(TARGET).o fake_xess.dll libfake_xess.so
rm -rf $(SAMPLE_OBJDIR) xess_loader_test_dir
.PHONY: all check clean
+318
View File
@@ -0,0 +1,318 @@
/* A stand-in XeSS library (libxess.dll on Windows, libxess.so on
* Linux): exports the entry points gfx/intel/xess_loader.c resolves,
* with the SDK's structures declared here again as xess.h, xess_d3d12.h
* and xess_vk.h have them (packed to 8, their natural layout), and
* records what it was asked (fake_xess.h). A context is a handle; the
* input resolution of a mode is by the SDK's ratios (XeSS 1.3 and up),
* the range a third of the output to the output. */
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "fake_xess.h"
#if defined(_WIN32)
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __attribute__((visibility("default")))
#endif
typedef struct { uint16_t major, minor, patch, reserved; } sdk_version;
typedef struct { uint32_t x, y; } sdk_2d;
typedef struct
{
sdk_2d outputResolution; int qualitySetting; uint32_t initFlags, creationNodeMask, visibleNodeMask;
void *pTempBufferHeap; uint64_t bufferHeapOffset; void *pTempTextureHeap; uint64_t textureHeapOffset; void *pPipelineLibrary;
} sdk_d3d12_init;
typedef struct
{
void *pColorTexture, *pVelocityTexture, *pDepthTexture, *pExposureScaleTexture, *pResponsivePixelMaskTexture, *pOutputTexture;
float jitterOffsetX, jitterOffsetY, exposureScale; uint32_t resetHistory, inputWidth, inputHeight;
sdk_2d inputColorBase, inputMotionVectorBase, inputDepthBase, inputResponsiveMaskBase, reserved0, outputColorBase;
void *pDescriptorHeap; uint32_t descriptorHeapOffset;
} sdk_d3d12_execute;
typedef struct
{
uint64_t imageView, image; struct { uint32_t aspectMask, baseMipLevel, levelCount, baseArrayLayer, layerCount; } subresourceRange;
int format; unsigned int width, height;
} sdk_vk_image_view_info;
typedef struct
{
sdk_vk_image_view_info colorTexture, velocityTexture, depthTexture, exposureScaleTexture, responsivePixelMaskTexture, outputTexture;
float jitterOffsetX, jitterOffsetY, exposureScale; uint32_t resetHistory, inputWidth, inputHeight;
sdk_2d inputColorBase, inputMotionVectorBase, inputDepthBase, inputResponsiveMaskBase, reserved0, outputColorBase;
} sdk_vk_execute;
typedef struct
{
sdk_2d outputResolution; int qualitySetting; uint32_t initFlags, creationNodeMask, visibleNodeMask;
uint64_t tempBufferHeap, bufferHeapOffset, tempTextureHeap, textureHeapOffset, pipelineCache;
} sdk_vk_init;
#define SUCCESS 0
#define ERROR_UNSUPPORTED_DEVICE -1
#define ERROR_INVALID_ARGUMENT -4
#define ERROR_DEVICE -6
#define ERROR_INVALID_CONTEXT -8
static fake_xess_record_t record;
static struct { int used; int vk; int inited; } contexts[FAKE_XESS_MAX_CALLS];
static const char *vk_instance_exts[] = { "VK_KHR_get_physical_device_properties2" };
static const char *vk_device_exts[] = { "VK_KHR_shader_float16_int8", "VK_KHR_16bit_storage" };
/* (a chain the device creation can take as it is: a features structure
* of the SDK's own, VkPhysicalDeviceShaderFloat16Int8Features, here
* asking for nothing so that any device takes it) */
static struct { uint32_t sType; void *pNext; uint32_t shaderFloat16, shaderInt8; } vk_features = { 1000082000u, NULL, 0, 0 };
EXPORT fake_xess_record_t *fake_xess_record(void) { return &record; }
static int valid(void *ctx)
{
return ctx >= (void*)&contexts[0] && ctx < (void*)&contexts[FAKE_XESS_MAX_CALLS]
&& ((int*)ctx)[0];
}
static int make_context(void **out, int vk)
{
int i;
record.creates++;
if (record.create_fails)
return ERROR_UNSUPPORTED_DEVICE;
for (i = 0; i < FAKE_XESS_MAX_CALLS; i++)
if (!contexts[i].used)
{
contexts[i].used = 1;
contexts[i].vk = vk;
contexts[i].inited = 0;
*out = &contexts[i];
return SUCCESS;
}
return ERROR_DEVICE;
}
EXPORT int xessGetVersion(sdk_version *v)
{
if (!v)
return ERROR_INVALID_ARGUMENT;
v->major = 2; v->minor = 0; v->patch = 1; v->reserved = 0;
return SUCCESS;
}
EXPORT int xessGetOptimalInputResolution(void *ctx, const sdk_2d *out, int quality, sdk_2d *optimal, sdk_2d *min, sdk_2d *max)
{
static const float ratios[7] = { 3.0f, 2.3f, 2.0f, 1.7f, 1.5f, 1.3f, 1.0f };
if (!valid(ctx) || !out || !optimal || !min || !max || quality < 100 || quality > 106)
return ERROR_INVALID_ARGUMENT;
optimal->x = (uint32_t)((float)out->x / ratios[quality - 100]);
optimal->y = (uint32_t)((float)out->y / ratios[quality - 100]);
min->x = out->x / 3; min->y = out->y / 3;
*max = *out;
return SUCCESS;
}
EXPORT int xessDestroyContext(void *ctx)
{
if (!valid(ctx))
return ERROR_INVALID_CONTEXT;
memset(ctx, 0, sizeof(contexts[0]));
record.destroys++;
return SUCCESS;
}
EXPORT int xessSetJitterScale(void *ctx, float x, float y)
{
if (!valid(ctx)) return ERROR_INVALID_CONTEXT;
record.jitter_scale_x = x; record.jitter_scale_y = y;
return SUCCESS;
}
EXPORT int xessSetVelocityScale(void *ctx, float x, float y)
{
if (!valid(ctx)) return ERROR_INVALID_CONTEXT;
record.velocity_scale_x = x; record.velocity_scale_y = y;
return SUCCESS;
}
EXPORT int xessSetExposureMultiplier(void *ctx, float scale)
{
if (!valid(ctx)) return ERROR_INVALID_CONTEXT;
record.exposure_multiplier = scale;
return SUCCESS;
}
EXPORT int xessGetPipelineBuildStatus(void *ctx)
{
if (!valid(ctx)) return ERROR_INVALID_CONTEXT;
if (record.build_pending)
{
record.build_pending--;
return -9;
}
return SUCCESS;
}
EXPORT int xessSetLoggingCallback(void *ctx, int level, void (*cb)(const char*, int))
{
if (!valid(ctx)) return ERROR_INVALID_CONTEXT;
record.log_level = level;
if (cb)
cb("fake xess: hello", 1);
return SUCCESS;
}
static void take_init(int vk, void *ctx, const sdk_2d *out, int quality, uint32_t flags, uint32_t mask)
{
if (record.inits < FAKE_XESS_MAX_CALLS)
{
fake_xess_init_t *i = &record.init[record.inits];
i->vk = vk; i->context = ctx; i->out_w = out->x; i->out_h = out->y;
i->quality = quality; i->flags = flags; i->node_mask = mask;
}
record.inits++;
}
/* ---- Direct3D 12 ---- */
EXPORT int xessD3D12CreateContext(void *device, void **ctx)
{
if (!device || !ctx)
return ERROR_INVALID_ARGUMENT;
record.create_device = device;
return make_context(ctx, 0);
}
EXPORT int xessD3D12BuildPipelines(void *ctx, void *library, unsigned char blocking, uint32_t flags)
{
(void)library; (void)flags;
if (!valid(ctx)) return ERROR_INVALID_CONTEXT;
record.pipelines++;
record.build_blocking = blocking;
return SUCCESS;
}
EXPORT int xessD3D12Init(void *ctx, const sdk_d3d12_init *p)
{
if (!valid(ctx) || !p)
return ERROR_INVALID_ARGUMENT;
take_init(0, ctx, &p->outputResolution, p->qualitySetting, p->initFlags, p->creationNodeMask);
if (record.init_fails)
return ERROR_DEVICE;
((int*)ctx)[2] = 1;
return SUCCESS;
}
static void take_d3d12_image(fake_xess_image_t *i, void *res)
{
memset(i, 0, sizeof(*i));
i->present = res != NULL;
i->image = (uint64_t)(uintptr_t)res;
}
EXPORT int xessD3D12Execute(void *ctx, void *list, const sdk_d3d12_execute *p)
{
if (!valid(ctx) || !p || !list)
return ERROR_INVALID_ARGUMENT;
if (!((int*)ctx)[2])
return -3;
if (record.executes < FAKE_XESS_MAX_CALLS)
{
fake_xess_execute_t *e = &record.execute[record.executes];
memset(e, 0, sizeof(*e));
e->vk = 0; e->context = ctx; e->command_list = list;
take_d3d12_image(&e->color, p->pColorTexture);
take_d3d12_image(&e->velocity, p->pVelocityTexture);
take_d3d12_image(&e->depth, p->pDepthTexture);
take_d3d12_image(&e->exposure, p->pExposureScaleTexture);
take_d3d12_image(&e->mask, p->pResponsivePixelMaskTexture);
take_d3d12_image(&e->output, p->pOutputTexture);
e->jitter_x = p->jitterOffsetX; e->jitter_y = p->jitterOffsetY; e->exposure_scale = p->exposureScale;
e->reset = p->resetHistory; e->input_w = p->inputWidth; e->input_h = p->inputHeight;
}
record.executes++;
return record.execute_fails ? ERROR_DEVICE : SUCCESS;
}
/* ---- Vulkan ---- */
EXPORT int xessVKGetRequiredInstanceExtensions(uint32_t *count, const char *const **exts, uint32_t *min_version)
{
if (!count || !exts || !min_version)
return ERROR_INVALID_ARGUMENT;
*count = 1; *exts = vk_instance_exts; *min_version = (1u << 22) | (1u << 12);
return SUCCESS;
}
EXPORT int xessVKGetRequiredDeviceExtensions(void *instance, void *gpu, uint32_t *count, const char *const **exts)
{
if (!instance || !gpu || !count || !exts)
return ERROR_INVALID_ARGUMENT;
*count = 2; *exts = vk_device_exts;
return SUCCESS;
}
EXPORT int xessVKGetRequiredDeviceFeatures(void *instance, void *gpu, void **features)
{
if (!instance || !gpu || !features)
return ERROR_INVALID_ARGUMENT;
*features = &vk_features;
return SUCCESS;
}
EXPORT int xessVKCreateContext(void *instance, void *gpu, void *device, void **ctx)
{
if (!instance || !gpu || !device || !ctx)
return ERROR_INVALID_ARGUMENT;
record.create_instance = instance; record.create_physical_device = gpu; record.create_device = device;
return make_context(ctx, 1);
}
EXPORT int xessVKBuildPipelines(void *ctx, uint64_t cache, unsigned char blocking, uint32_t flags)
{
(void)cache; (void)flags;
if (!valid(ctx)) return ERROR_INVALID_CONTEXT;
record.pipelines++;
record.build_blocking = blocking;
return SUCCESS;
}
EXPORT int xessVKInit(void *ctx, const sdk_vk_init *p)
{
if (!valid(ctx) || !p)
return ERROR_INVALID_ARGUMENT;
take_init(1, ctx, &p->outputResolution, p->qualitySetting, p->initFlags, p->creationNodeMask);
if (record.init_fails)
return ERROR_DEVICE;
((int*)ctx)[2] = 1;
return SUCCESS;
}
static void take_vk_image(fake_xess_image_t *i, const sdk_vk_image_view_info *v)
{
i->present = v->image != 0;
i->image = v->image; i->view = v->imageView;
i->aspect = v->subresourceRange.aspectMask; i->format = v->format;
i->width = v->width; i->height = v->height;
}
EXPORT int xessVKExecute(void *ctx, void *cmd, const sdk_vk_execute *p)
{
if (!valid(ctx) || !p || !cmd)
return ERROR_INVALID_ARGUMENT;
if (!((int*)ctx)[2])
return -3;
if (record.executes < FAKE_XESS_MAX_CALLS)
{
fake_xess_execute_t *e = &record.execute[record.executes];
memset(e, 0, sizeof(*e));
e->vk = 1; e->context = ctx; e->command_list = cmd;
take_vk_image(&e->color, &p->colorTexture);
take_vk_image(&e->velocity, &p->velocityTexture);
take_vk_image(&e->depth, &p->depthTexture);
take_vk_image(&e->exposure, &p->exposureScaleTexture);
take_vk_image(&e->mask, &p->responsivePixelMaskTexture);
take_vk_image(&e->output, &p->outputTexture);
e->jitter_x = p->jitterOffsetX; e->jitter_y = p->jitterOffsetY; e->exposure_scale = p->exposureScale;
e->reset = p->resetHistory; e->input_w = p->inputWidth; e->input_h = p->inputHeight;
}
record.executes++;
return record.execute_fails ? ERROR_DEVICE : SUCCESS;
}
+57
View File
@@ -0,0 +1,57 @@
/* What the stand-in XeSS library (fake_xess.c) saw: read by a test
* through its exported fake_xess_record(). Plain C, no SDK. */
#ifndef FAKE_XESS_H
#define FAKE_XESS_H
#include <stdint.h>
#define FAKE_XESS_MAX_CALLS 8
typedef struct fake_xess_init
{
int vk; /* Vulkan's init, or Direct3D 12's */
void *context;
uint32_t out_w, out_h;
int quality;
uint32_t flags;
uint32_t node_mask;
} fake_xess_init_t;
typedef struct fake_xess_image
{
int present;
uint64_t image; /* Direct3D 12: the resource */
uint64_t view;
uint32_t aspect, width, height;
int format;
} fake_xess_image_t;
typedef struct fake_xess_execute
{
int vk;
void *context;
void *command_list;
fake_xess_image_t color, velocity, depth, exposure, mask, output;
float jitter_x, jitter_y, exposure_scale;
uint32_t reset, input_w, input_h;
} fake_xess_execute_t;
typedef struct fake_xess_record
{
unsigned creates, inits, executes, destroys, pipelines;
fake_xess_init_t init[FAKE_XESS_MAX_CALLS];
fake_xess_execute_t execute[FAKE_XESS_MAX_CALLS];
void *create_device, *create_instance, *create_physical_device;
float velocity_scale_x, velocity_scale_y;
float jitter_scale_x, jitter_scale_y;
float exposure_multiplier;
int log_level;
/* the test's knobs */
int create_fails; /* XESS_RESULT_ERROR_UNSUPPORTED_DEVICE */
int init_fails;
int execute_fails;
int build_blocking; /* what the last build_pipelines was asked */
int build_pending; /* get_pipeline_build_status: this many more calls in progress */
} fake_xess_record_t;
#endif
+294
View File
@@ -0,0 +1,294 @@
/* gfx/intel/xess_loader.c against a stand-in XeSS library (fake_xess.c)
* in a directory of the test's: the parameter layouts pinned to the
* SDK's, the library found, every entry point of each API resolved,
* the version, the optimal resolution of a quality, a context made,
* initialised and executed on each API with its parameters read back
* whole, the Vulkan extension and feature queries. C89. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gfx/intel/xess_loader.h>
#include <file/file_path.h>
#if defined(_WIN32)
#include <windows.h>
#else
#include <dlfcn.h>
#endif
#include "fake_xess.h"
/* The SDK's sizes and offsets on x86-64 (printed from its headers) */
#define LAYOUT(name, cond) typedef char layout_##name[(cond) ? 1 : -1]
LAYOUT(version, sizeof(xess_version_t) == 8);
LAYOUT(d3d12_init, sizeof(xess_d3d12_init_params_t) == 64);
LAYOUT(d3d12_exec, sizeof(xess_d3d12_execute_params_t) == 136);
LAYOUT(d3d12_jit, offsetof(xess_d3d12_execute_params_t, jitter_offset_x) == 48);
LAYOUT(d3d12_base, offsetof(xess_d3d12_execute_params_t, input_color_base) == 72);
LAYOUT(d3d12_heap, offsetof(xess_d3d12_execute_params_t, descriptor_heap) == 120);
LAYOUT(vk_view, sizeof(xess_vk_image_view_info_t) == 48);
LAYOUT(vk_format, offsetof(xess_vk_image_view_info_t, format) == 36);
LAYOUT(vk_exec, sizeof(xess_vk_execute_params_t) == 360);
LAYOUT(vk_jit, offsetof(xess_vk_execute_params_t, jitter_offset_x) == 288);
LAYOUT(vk_init, sizeof(xess_vk_init_params_t) == 64);
LAYOUT(vk_heap, offsetof(xess_vk_init_params_t, temp_buffer_heap) == 24);
static int failures;
#define CHECK(cond) \
do { \
if (!(cond)) \
{ \
printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); \
failures++; \
} \
} while (0)
static fake_xess_record_t *record_of(const xess_lib_t *lib)
{
fake_xess_record_t *(*get)(void) = NULL;
#if defined(_WIN32)
FARPROC p = GetProcAddress((HMODULE)lib->handle, "fake_xess_record");
#else
void *p = dlsym(lib->handle, "fake_xess_record");
#endif
memcpy(&get, &p, sizeof(get));
return get ? get() : NULL;
}
/* The stand-in copied into @dir under the library's name */
static int plant(const char *dir)
{
char path[256];
FILE *in, *out;
char buf[4096];
size_t n;
path_mkdir(dir);
#if defined(_WIN32)
in = fopen("fake_xess.dll", "rb");
sprintf(path, "%s/libxess.dll", dir);
#else
in = fopen("libfake_xess.so", "rb");
sprintf(path, "%s/libxess.so", dir);
#endif
if (!in)
return 0;
if (!(out = fopen(path, "wb")))
{
fclose(in);
return 0;
}
while ((n = fread(buf, 1, sizeof(buf), in)) > 0)
fwrite(buf, 1, n, out);
fclose(in);
fclose(out);
return 1;
}
static int logged;
static void logger(const char *message, int level)
{
(void)message;
(void)level;
logged++;
}
static void vk_view(xess_vk_image_view_info_t *v, uint64_t image, uint64_t view,
uint32_t aspect, int format, uint32_t w, uint32_t h)
{
memset(v, 0, sizeof(*v));
v->image = image;
v->image_view = view;
v->subresource_range.aspect_mask = aspect;
v->subresource_range.level_count = 1;
v->subresource_range.layer_count = 1;
v->format = format;
v->width = w;
v->height = h;
}
int main(void)
{
const xess_lib_t *lib;
fake_xess_record_t *rec;
xess_context_t ctx = NULL;
xess_version_t version;
xess_2d_t out, optimal, min, max;
xess_d3d12_init_params_t d3d12_init;
xess_d3d12_execute_params_t d3d12_exec;
xess_vk_init_params_t vk_init;
xess_vk_execute_params_t vk_exec;
int fake_device = 0, fake_list = 0, fake_instance = 0, fake_gpu = 0;
int fake_color = 0, fake_mv = 0, fake_depth = 0, fake_out = 0;
uint32_t count = 0, min_api = 0;
const char *const *exts = NULL;
void *features = NULL;
/* Nothing in an empty or missing directory, and nothing loaded */
CHECK(!xess_runtime_in(""));
CHECK(!xess_runtime_in("xess_loader_test_none"));
CHECK(!strcmp(xess_lib_status(), "not probed"));
CHECK(xess_lib_get("") == NULL);
CHECK(!strcmp(xess_lib_status(), "no XeSS directory"));
CHECK(xess_lib_get("xess_loader_test_none") == NULL);
CHECK(strstr(xess_lib_status(), "no XeSS library") != NULL);
CHECK(strstr(xess_lib_status(), "xess_loader_test_none") != NULL);
CHECK(xess_lib_get("xess_loader_test_none") == NULL);
/* The library, every entry point, its version, in the status */
CHECK(plant("xess_loader_test_dir"));
CHECK(xess_runtime_in("xess_loader_test_dir"));
lib = xess_lib_get("xess_loader_test_dir");
CHECK(lib != NULL);
if (!lib)
return 1;
CHECK(xess_lib_get("xess_loader_test_none") == lib);
CHECK(lib->common.get_version && lib->common.get_optimal_input_resolution
&& lib->common.destroy_context && lib->common.set_jitter_scale
&& lib->common.set_velocity_scale && lib->common.set_exposure_multiplier
&& lib->common.set_logging_callback && lib->common.get_pipeline_build_status);
CHECK(lib->d3d12.create_context && lib->d3d12.build_pipelines && lib->d3d12.init && lib->d3d12.execute);
CHECK(lib->vk.get_required_instance_extensions && lib->vk.get_required_device_extensions
&& lib->vk.get_required_device_features && lib->vk.create_context
&& lib->vk.build_pipelines && lib->vk.init && lib->vk.execute);
CHECK(lib->version.major == 2 && lib->version.minor == 0 && lib->version.patch == 1);
CHECK(strstr(xess_lib_status(), "XeSS library ") == xess_lib_status());
CHECK(strstr(xess_lib_status(), "xess_loader_test_dir") != NULL);
CHECK(strstr(xess_lib_status(), " 2.0.1") != NULL);
CHECK(strstr(xess_lib_status(), "Vulkan and Direct3D 12") != NULL);
rec = record_of(lib);
CHECK(rec != NULL);
if (!rec)
return 1;
CHECK(lib->common.get_version(&version) == XESS_RESULT_SUCCESS && version.major == 2);
/* Direct3D 12: a context, its pipelines, init, execute */
CHECK(lib->d3d12.create_context(&fake_device, &ctx) == XESS_RESULT_SUCCESS && ctx != NULL);
CHECK(rec->creates == 1 && rec->create_device == &fake_device);
/* The optimal input resolution of a quality, of a context */
out.x = 1920;
out.y = 1080;
CHECK(lib->common.get_optimal_input_resolution(ctx, &out, XESS_QUALITY_BALANCED,
&optimal, &min, &max) == XESS_RESULT_SUCCESS);
CHECK(optimal.x == 960 && optimal.y == 540);
CHECK(min.x == 640 && min.y == 360 && max.x == 1920 && max.y == 1080);
CHECK(lib->common.get_optimal_input_resolution(ctx, &out, XESS_QUALITY_AA,
&optimal, &min, &max) == XESS_RESULT_SUCCESS && optimal.x == 1920);
CHECK(XESS_FAILED(lib->common.get_optimal_input_resolution(ctx, &out, 99,
&optimal, &min, &max)));
CHECK(lib->common.set_logging_callback(ctx, XESS_LOGGING_LEVEL_WARNING, logger) == XESS_RESULT_SUCCESS);
CHECK(rec->log_level == XESS_LOGGING_LEVEL_WARNING && logged == 1);
CHECK(lib->d3d12.build_pipelines(ctx, NULL, false, XESS_INIT_FLAG_LDR_INPUT_COLOR) == XESS_RESULT_SUCCESS);
CHECK(rec->pipelines == 1 && !rec->build_blocking);
rec->build_pending = 2;
CHECK(lib->common.get_pipeline_build_status(ctx) == XESS_RESULT_ERROR_OPERATION_IN_PROGRESS);
CHECK(lib->common.get_pipeline_build_status(ctx) == XESS_RESULT_ERROR_OPERATION_IN_PROGRESS);
CHECK(lib->common.get_pipeline_build_status(ctx) == XESS_RESULT_SUCCESS);
memset(&d3d12_init, 0, sizeof(d3d12_init));
d3d12_init.output_resolution.x = 1920;
d3d12_init.output_resolution.y = 1080;
d3d12_init.quality = XESS_QUALITY_BALANCED;
d3d12_init.init_flags = XESS_INIT_FLAG_LDR_INPUT_COLOR | XESS_INIT_FLAG_INVERTED_DEPTH;
d3d12_init.creation_node_mask = 1;
d3d12_init.visible_node_mask = 1;
/* (not initialised yet: an execute is refused) */
memset(&d3d12_exec, 0, sizeof(d3d12_exec));
d3d12_exec.color = &fake_color;
d3d12_exec.output = &fake_out;
CHECK(lib->d3d12.execute(ctx, &fake_list, &d3d12_exec) == XESS_RESULT_ERROR_UNINITIALIZED);
CHECK(lib->d3d12.init(ctx, &d3d12_init) == XESS_RESULT_SUCCESS);
CHECK(rec->inits == 1 && !rec->init[0].vk && rec->init[0].context == ctx);
CHECK(rec->init[0].out_w == 1920 && rec->init[0].out_h == 1080);
CHECK(rec->init[0].quality == XESS_QUALITY_BALANCED);
CHECK(rec->init[0].flags == (XESS_INIT_FLAG_LDR_INPUT_COLOR | XESS_INIT_FLAG_INVERTED_DEPTH));
CHECK(rec->init[0].node_mask == 1);
CHECK(lib->common.set_velocity_scale(ctx, 1.0f, -1.0f) == XESS_RESULT_SUCCESS);
CHECK(rec->velocity_scale_x == 1.0f && rec->velocity_scale_y == -1.0f);
CHECK(lib->common.set_jitter_scale(ctx, 2.0f, 2.0f) == XESS_RESULT_SUCCESS);
CHECK(rec->jitter_scale_x == 2.0f);
CHECK(lib->common.set_exposure_multiplier(ctx, 0.5f) == XESS_RESULT_SUCCESS);
CHECK(rec->exposure_multiplier == 0.5f);
d3d12_exec.velocity = &fake_mv;
d3d12_exec.depth = &fake_depth;
d3d12_exec.jitter_offset_x = 0.25f;
d3d12_exec.jitter_offset_y = -0.125f;
d3d12_exec.exposure_scale_value = 1.0f;
d3d12_exec.reset_history = 1;
d3d12_exec.input_width = 1129;
d3d12_exec.input_height = 635;
CHECK(lib->d3d12.execute(ctx, &fake_list, &d3d12_exec) == XESS_RESULT_SUCCESS);
CHECK(rec->executes == 1 && !rec->execute[0].vk && rec->execute[0].context == ctx);
CHECK(rec->execute[0].command_list == &fake_list);
CHECK(rec->execute[0].color.present && rec->execute[0].color.image == (uint64_t)(uintptr_t)&fake_color);
CHECK(rec->execute[0].velocity.present && rec->execute[0].depth.present && rec->execute[0].output.present);
CHECK(!rec->execute[0].exposure.present && !rec->execute[0].mask.present);
CHECK(rec->execute[0].jitter_x == 0.25f && rec->execute[0].jitter_y == -0.125f);
CHECK(rec->execute[0].exposure_scale == 1.0f && rec->execute[0].reset == 1);
CHECK(rec->execute[0].input_w == 1129 && rec->execute[0].input_h == 635);
rec->execute_fails = 1;
CHECK(XESS_FAILED(lib->d3d12.execute(ctx, &fake_list, &d3d12_exec)));
rec->execute_fails = 0;
CHECK(lib->common.destroy_context(ctx) == XESS_RESULT_SUCCESS && rec->destroys == 1);
ctx = NULL;
rec->create_fails = 1;
CHECK(lib->d3d12.create_context(&fake_device, &ctx) == XESS_RESULT_ERROR_UNSUPPORTED_DEVICE && ctx == NULL);
rec->create_fails = 0;
/* Vulkan: what the device needs, a context, init, execute */
CHECK(lib->vk.get_required_instance_extensions(&count, &exts, &min_api) == XESS_RESULT_SUCCESS);
CHECK(count == 1 && exts && !strcmp(exts[0], "VK_KHR_get_physical_device_properties2"));
CHECK(min_api >= (1u << 22));
CHECK(lib->vk.get_required_device_extensions(&fake_instance, &fake_gpu, &count, &exts) == XESS_RESULT_SUCCESS);
CHECK(count == 2 && exts && !strcmp(exts[0], "VK_KHR_shader_float16_int8")
&& !strcmp(exts[1], "VK_KHR_16bit_storage"));
CHECK(lib->vk.get_required_device_features(&fake_instance, &fake_gpu, &features) == XESS_RESULT_SUCCESS);
CHECK(features != NULL && *(uint32_t*)features == 1000082000u);
CHECK(lib->vk.create_context(&fake_instance, &fake_gpu, &fake_device, &ctx) == XESS_RESULT_SUCCESS && ctx != NULL);
CHECK(rec->creates == 3 && rec->create_instance == &fake_instance
&& rec->create_physical_device == &fake_gpu && rec->create_device == &fake_device);
CHECK(lib->vk.build_pipelines(ctx, 0, true, 0) == XESS_RESULT_SUCCESS);
CHECK(rec->pipelines == 2 && rec->build_blocking);
memset(&vk_init, 0, sizeof(vk_init));
vk_init.output_resolution.x = 1280;
vk_init.output_resolution.y = 720;
vk_init.quality = XESS_QUALITY_ULTRA_QUALITY;
vk_init.init_flags = XESS_INIT_FLAG_JITTERED_MV;
rec->init_fails = 1;
CHECK(XESS_FAILED(lib->vk.init(ctx, &vk_init)));
rec->init_fails = 0;
CHECK(lib->vk.init(ctx, &vk_init) == XESS_RESULT_SUCCESS);
CHECK(rec->inits == 3 && rec->init[2].vk && rec->init[2].context == ctx);
CHECK(rec->init[2].out_w == 1280 && rec->init[2].out_h == 720);
CHECK(rec->init[2].quality == XESS_QUALITY_ULTRA_QUALITY && rec->init[2].flags == XESS_INIT_FLAG_JITTERED_MV);
memset(&vk_exec, 0, sizeof(vk_exec));
vk_view(&vk_exec.color, 0x1001, 0x2001, 1, 37, 853, 480);
vk_view(&vk_exec.velocity, 0x1002, 0x2002, 1, 83, 853, 480);
vk_view(&vk_exec.depth, 0x1003, 0x2003, 2, 126, 853, 480);
vk_view(&vk_exec.output, 0x1004, 0x2004, 1, 37, 1280, 720);
vk_exec.jitter_offset_x = -0.5f;
vk_exec.jitter_offset_y = 0.5f;
vk_exec.exposure_scale_value = 1.0f;
vk_exec.input_width = 853;
vk_exec.input_height = 480;
CHECK(lib->vk.execute(ctx, &fake_list, &vk_exec) == XESS_RESULT_SUCCESS);
CHECK(rec->executes == 3 && rec->execute[2].vk && rec->execute[2].context == ctx);
CHECK(rec->execute[2].color.present && rec->execute[2].color.image == 0x1001
&& rec->execute[2].color.view == 0x2001 && rec->execute[2].color.aspect == 1
&& rec->execute[2].color.format == 37 && rec->execute[2].color.width == 853
&& rec->execute[2].color.height == 480);
CHECK(rec->execute[2].depth.format == 126 && rec->execute[2].depth.aspect == 2);
CHECK(rec->execute[2].output.image == 0x1004 && rec->execute[2].output.width == 1280);
CHECK(!rec->execute[2].exposure.present && !rec->execute[2].mask.present);
CHECK(rec->execute[2].jitter_x == -0.5f && rec->execute[2].jitter_y == 0.5f);
CHECK(rec->execute[2].reset == 0 && rec->execute[2].input_w == 853);
CHECK(lib->common.destroy_context(ctx) == XESS_RESULT_SUCCESS && rec->destroys == 2);
CHECK(XESS_FAILED(lib->common.destroy_context(ctx)));
if (failures)
{
printf("xess_loader_test: %d failure(s)\n", failures);
return 1;
}
printf("xess_loader_test: ok\n");
return 0;
}