mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-09 03:29:43 -10:00
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:
1 parent
aca5868b04
commit
b6296db205
12 files changed
+2453
No files matched your search
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user