From 34b7ef03b55413f146e2a01ddd09175b23d37730 Mon Sep 17 00:00:00 2001 From: #xoxor4d Date: Tue, 16 Jul 2024 21:45:46 +0200 Subject: [PATCH 1/2] add bridgeApi review fixes (cherry picked from commit e6c64ef47b1e6d5f160978d6a8ada5e6aeddf1e7) --- bridge.conf | 8 + src/api/remix_api/bridge_c.h | 404 +++++++++++++++ src/api/remix_api/remix_c.h | 829 +++++++++++++++++++++++++++++++ src/client/client_options.h | 4 + src/client/d3d9_device.cpp | 6 + src/client/meson.build | 5 +- src/client/remix_api.cpp | 507 +++++++++++++++++++ src/client/remix_api.h | 29 ++ src/server/main.cpp | 553 +++++++++++++++++++++ src/server/meson.build | 4 +- src/server/module_processing.cpp | 21 + src/server/module_processing.h | 14 +- src/util/util_commands.h | 38 +- 13 files changed, 2418 insertions(+), 4 deletions(-) create mode 100644 src/api/remix_api/bridge_c.h create mode 100644 src/api/remix_api/remix_c.h create mode 100644 src/client/remix_api.cpp create mode 100644 src/client/remix_api.h diff --git a/bridge.conf b/bridge.conf index 49b562a..3ff01a6 100644 --- a/bridge.conf +++ b/bridge.conf @@ -109,6 +109,14 @@ # client.enableDpiAwareness = True +# Exposes the RemixApi to the client allowing 32-bit games +# to use parts of the RemixApi. +# +# Supported values: True, False + +# client.exposeRemixApi = False + + # # Server Settings # diff --git a/src/api/remix_api/bridge_c.h b/src/api/remix_api/bridge_c.h new file mode 100644 index 0000000..ac58d71 --- /dev/null +++ b/src/api/remix_api/bridge_c.h @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ + +// Bridge API header for both the x86 bridge client and the x86 game + +#ifndef BRIDGE_C_H_ +#define BRIDGE_C_H_ + +#include +#include + +#define BRIDGEAPI_CALL __stdcall +#define BRIDGEAPI_PTR BRIDGEAPI_CALL + +#ifdef BRIDGE_API_IMPORT + #define BRIDGE_API __declspec(dllimport) +#else + #define BRIDGE_API __declspec(dllexport) +#endif + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + typedef enum bridgeapi_ErrorCode { + BRIDGEAPI_ERROR_CODE_SUCCESS = 0, + BRIDGEAPI_ERROR_CODE_GENERAL_FAILURE = 1, + // WinAPI's GetModuleHandle has failed + BRIDGEAPI_ERROR_CODE_GET_MODULE_HANDLE_FAILURE = 2, + BRIDGEAPI_ERROR_CODE_INVALID_ARGUMENTS = 3, + // Couldn't find 'remixInitialize' function in the .dll + BRIDGEAPI_ERROR_CODE_GET_PROC_ADDRESS_FAILURE = 4, + // CreateD3D9 / RegisterD3D9Device can be called only once + BRIDGEAPI_ERROR_CODE_ALREADY_EXISTS = 5, + // RegisterD3D9Device requires the device that was created with IDirect3DDevice9Ex, returned by CreateD3D9 + BRIDGEAPI_ERROR_CODE_REGISTERING_NON_REMIX_D3D9_DEVICE = 6, + // RegisterD3D9Device was not called + BRIDGEAPI_ERROR_CODE_REMIX_DEVICE_WAS_NOT_REGISTERED = 7, + BRIDGEAPI_ERROR_CODE_INCOMPATIBLE_VERSION = 8, + // WinAPI's SetDllDirectory has failed + //BRIDGEAPI_ERROR_CODE_SET_DLL_DIRECTORY_FAILURE = 9, + // WinAPI's GetFullPathName has failed + //BRIDGEAPI_ERROR_CODE_GET_FULL_PATH_NAME_FAILURE = 10, + BRIDGEAPI_ERROR_CODE_NOT_INITIALIZED = 11, + } BRIDGEAPI_ErrorCode; + + // ------------------------------------------ + // <- remix api types from the remix_c header + + typedef enum remixapi_StructType { + REMIXAPI_STRUCT_TYPE_NONE = 0, + REMIXAPI_STRUCT_TYPE_INITIALIZE_LIBRARY_INFO = 1, + REMIXAPI_STRUCT_TYPE_MATERIAL_INFO = 2, + REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_PORTAL_EXT = 3, + REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_TRANSLUCENT_EXT = 4, + REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_OPAQUE_EXT = 5, + REMIXAPI_STRUCT_TYPE_LIGHT_INFO = 6, + REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DISTANT_EXT = 7, + REMIXAPI_STRUCT_TYPE_LIGHT_INFO_CYLINDER_EXT = 8, + REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DISK_EXT = 9, + REMIXAPI_STRUCT_TYPE_LIGHT_INFO_RECT_EXT = 10, + REMIXAPI_STRUCT_TYPE_LIGHT_INFO_SPHERE_EXT = 11, + REMIXAPI_STRUCT_TYPE_MESH_INFO = 12, + REMIXAPI_STRUCT_TYPE_INSTANCE_INFO = 13, + REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_BONE_TRANSFORMS_EXT = 14, + REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_BLEND_EXT = 15, + REMIXAPI_STRUCT_TYPE_CAMERA_INFO = 16, + REMIXAPI_STRUCT_TYPE_CAMERA_INFO_PARAMETERIZED_EXT = 17, + REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_OPAQUE_SUBSURFACE_EXT = 18, + REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_OBJECT_PICKING_EXT = 19, + REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DOME_EXT = 20, + REMIXAPI_STRUCT_TYPE_LIGHT_INFO_USD_EXT = 21, + REMIXAPI_STRUCT_TYPE_STARTUP_INFO = 22, + REMIXAPI_STRUCT_TYPE_PRESENT_INFO = 23, + // NOTE: if adding a new struct, register it in 'rtx_remix_specialization.inl' + } remixapi_StructType; + + namespace x86 + { + typedef uint32_t remixapi_Bool; + + typedef struct remixapi_Rect2D { + int32_t left; + int32_t top; + int32_t right; + int32_t bottom; + } remixapi_Rect2D; + + typedef struct remixapi_Float2D { + float x; + float y; + } remixapi_Float2D; + + typedef struct remixapi_Float3D { + float x; + float y; + float z; + } remixapi_Float3D; + + typedef struct remixapi_Float4D { + float x; + float y; + float z; + float w; + } remixapi_Float4D; + + typedef struct remixapi_Transform { + float matrix[3][4]; + } remixapi_Transform; + + //typedef struct remixapi_MaterialHandle_T* remixapi_MaterialHandle; + //typedef struct remixapi_MeshHandle_T* remixapi_MeshHandle; + //typedef struct remixapi_LightHandle_T* remixapi_LightHandle; + + typedef const wchar_t* remixapi_Path; + + typedef struct remixapi_MaterialInfoOpaqueEXT { + remixapi_StructType sType; + //void* pNext; + remixapi_Path roughnessTexture; + remixapi_Path metallicTexture; + float anisotropy; + remixapi_Float3D albedoConstant; + float opacityConstant; + float roughnessConstant; + float metallicConstant; + remixapi_Bool thinFilmThickness_hasvalue; + float thinFilmThickness_value; + remixapi_Bool alphaIsThinFilmThickness; + remixapi_Path heightTexture; + float heightTextureStrength; + // If true, InstanceInfoBlendEXT is used as a source for alpha state + remixapi_Bool useDrawCallAlphaState; + remixapi_Bool blendType_hasvalue; + int blendType_value; + remixapi_Bool invertedBlend; + int alphaTestType; + uint8_t alphaReferenceValue; + } remixapi_MaterialInfoOpaqueEXT; + + // Valid only if remixapi_MaterialInfo contains remixapi_MaterialInfoOpaqueEXT in pNext chain + typedef struct remixapi_MaterialInfoOpaqueSubsurfaceEXT { + remixapi_StructType sType; + //void* pNext; + remixapi_Path subsurfaceTransmittanceTexture; + remixapi_Path subsurfaceThicknessTexture; + remixapi_Path subsurfaceSingleScatteringAlbedoTexture; + remixapi_Float3D subsurfaceTransmittanceColor; + float subsurfaceMeasurementDistance; + remixapi_Float3D subsurfaceSingleScatteringAlbedo; + float subsurfaceVolumetricAnisotropy; + } remixapi_MaterialInfoOpaqueSubsurfaceEXT; + + typedef struct remixapi_MaterialInfoTranslucentEXT { + remixapi_StructType sType; + //void* pNext; + remixapi_Path transmittanceTexture; + float refractiveIndex; + remixapi_Float3D transmittanceColor; + float transmittanceMeasurementDistance; + remixapi_Bool thinWallThickness_hasvalue; + float thinWallThickness_value; + remixapi_Bool useDiffuseLayer; + } remixapi_MaterialInfoTranslucentEXT; + + typedef struct remixapi_MaterialInfoPortalEXT { + remixapi_StructType sType; + //void* pNext; + uint8_t rayPortalIndex; + float rotationSpeed; + } remixapi_MaterialInfoPortalEXT; + + typedef struct remixapi_MaterialInfo { + remixapi_StructType sType; + //void* pNext; + uint64_t hash; + remixapi_Path albedoTexture; + remixapi_Path normalTexture; + remixapi_Path tangentTexture; + remixapi_Path emissiveTexture; + float emissiveIntensity; + remixapi_Float3D emissiveColorConstant; + uint8_t spriteSheetRow; + uint8_t spriteSheetCol; + uint8_t spriteSheetFps; + uint8_t filterMode; // Nearest: 0 Linear: 1 + uint8_t wrapModeU; // Clamp: 0 Repeat: 1 Mirrored_Repeat: 2 Clip: 3 + uint8_t wrapModeV; // Clamp: 0 Repeat: 1 Mirrored_Repeat: 2 Clip: 3 + } remixapi_MaterialInfo; + + + typedef struct remixapi_HardcodedVertex { + float position[3]; + float normal[3]; + float texcoord[2]; + uint32_t color; + uint32_t _pad0; + uint32_t _pad1; + uint32_t _pad2; + uint32_t _pad3; + uint32_t _pad4; + uint32_t _pad5; + uint32_t _pad6; + } remixapi_HardcodedVertex; + + // # TODO remixapi_MeshInfoSkinning + + typedef struct remixapi_MeshInfoSurfaceTriangles { + const remixapi_HardcodedVertex* vertices_values; + uint64_t vertices_count; + const uint32_t* indices_values; + uint64_t indices_count; + remixapi_Bool skinning_hasvalue; + //remixapi_MeshInfoSkinning skinning_value; // # TODO + uint64_t material; // CHANGED - was remixapi_MaterialHandle + } remixapi_MeshInfoSurfaceTriangles; + + typedef struct remixapi_MeshInfo { + remixapi_StructType sType; + //void* pNext; + uint64_t hash; + const remixapi_MeshInfoSurfaceTriangles* surfaces_values; + uint32_t surfaces_count; + } remixapi_MeshInfo; + + + typedef struct remixapi_LightInfoLightShaping { + // The direction the Light Shaping is pointing in. Must be normalized. + remixapi_Float3D direction; + float coneAngleDegrees; + float coneSoftness; + float focusExponent; + } remixapi_LightInfoLightShaping; + + typedef struct remixapi_LightInfoSphereEXT { + remixapi_StructType sType; + //void* pNext; + remixapi_Float3D position; + float radius; + remixapi_Bool shaping_hasvalue; + remixapi_LightInfoLightShaping shaping_value; + } remixapi_LightInfoSphereEXT; + + typedef struct remixapi_LightInfoRectEXT { + remixapi_StructType sType; + //void* pNext; + remixapi_Float3D position; + // The X axis of the Rect Light. Must be normalized and orthogonal to the Y and direction axes. + remixapi_Float3D xAxis; + float xSize; + // The Y axis of the Rect Light. Must be normalized and orthogonal to the X and direction axes. + remixapi_Float3D yAxis; + float ySize; + // The direction the Rect Light is pointing in, should match the Shaping direction if present. + // Must be normalized and orthogonal to the X and Y axes. + remixapi_Float3D direction; + remixapi_Bool shaping_hasvalue; + remixapi_LightInfoLightShaping shaping_value; + } remixapi_LightInfoRectEXT; + + typedef struct remixapi_LightInfoDiskEXT { + remixapi_StructType sType; + //void* pNext; + remixapi_Float3D position; + // The X axis of the Disk Light. Must be normalized and orthogonal to the Y and direction axes. + remixapi_Float3D xAxis; + float xRadius; + // The Y axis of the Disk Light. Must be normalized and orthogonal to the X and direction axes. + remixapi_Float3D yAxis; + float yRadius; + // The direction the Disk Light is pointing in, should match the Shaping direction if present + // Must be normalized and orthogonal to the X and Y axes. + remixapi_Float3D direction; + remixapi_Bool shaping_hasvalue; + remixapi_LightInfoLightShaping shaping_value; + } remixapi_LightInfoDiskEXT; + + typedef struct remixapi_LightInfoCylinderEXT { + remixapi_StructType sType; + //void* pNext; + remixapi_Float3D position; + float radius; + // The "center" axis of the Cylinder Light. Must be normalized. + remixapi_Float3D axis; + float axisLength; + } remixapi_LightInfoCylinderEXT; + + typedef struct remixapi_LightInfoDistantEXT { + remixapi_StructType sType; + //void* pNext; + // The direction the Distant Light is pointing in. Must be normalized. + remixapi_Float3D direction; + float angularDiameterDegrees; + } remixapi_LightInfoDistantEXT; + + typedef struct remixapi_LightInfo { + remixapi_StructType sType; + //void* pNext; + uint64_t hash; + remixapi_Float3D radiance; + } remixapi_LightInfo; + } + + // -------------------------------------------- + // remix api types end -> + + typedef void(BRIDGEAPI_PTR* PFN_bridgeapi_DebugPrint)(const char* text); + typedef uint64_t(BRIDGEAPI_PTR* PFN_bridgeapi_CreateOpaqueMaterial)(const x86::remixapi_MaterialInfo* info, const x86::remixapi_MaterialInfoOpaqueEXT* ext, const x86::remixapi_MaterialInfoOpaqueSubsurfaceEXT* ext_ss); + typedef uint64_t(BRIDGEAPI_PTR* PFN_bridgeapi_CreateTranslucentMaterial)(const x86::remixapi_MaterialInfo* info, const x86::remixapi_MaterialInfoTranslucentEXT* ext); + typedef uint64_t(BRIDGEAPI_PTR* PFN_bridgeapi_CreatePortalMaterial)(const x86::remixapi_MaterialInfo* info, const x86::remixapi_MaterialInfoPortalEXT* ext); + typedef void(BRIDGEAPI_PTR* PFN_bridgeapi_DestroyMaterial)(uint64_t handle); + typedef uint64_t(BRIDGEAPI_PTR* PFN_bridgeapi_CreateTriangleMesh)(const x86::remixapi_MeshInfo* info); + typedef void(BRIDGEAPI_PTR* PFN_bridgeapi_DestroyMesh)(uint64_t handle); + typedef void(BRIDGEAPI_PTR* PFN_bridgeapi_DrawMeshInstance)(uint64_t handle, const x86::remixapi_Transform* t, x86::remixapi_Bool double_sided); + typedef uint64_t(BRIDGEAPI_PTR* PFN_bridgeapi_CreateSphereLight)(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoSphereEXT* ext); + typedef uint64_t(BRIDGEAPI_PTR* PFN_bridgeapi_CreateRectLight)(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoRectEXT* ext); + typedef uint64_t(BRIDGEAPI_PTR* PFN_bridgeapi_CreateDiskLight)(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoDiskEXT* ext); + typedef uint64_t(BRIDGEAPI_PTR* PFN_bridgeapi_CreateCylinderLight)(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoCylinderEXT* ext); + typedef uint64_t(BRIDGEAPI_PTR* PFN_bridgeapi_CreateDistantLight)(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoDistantEXT* ext); + typedef void(BRIDGEAPI_PTR* PFN_bridgeapi_DestroyLight)(uint64_t handle); + typedef void(BRIDGEAPI_PTR* PFN_bridgeapi_DrawLightInstance)(uint64_t handle); + typedef void(BRIDGEAPI_PTR* PFN_bridgeapi_SetConfigVariable)(const char* var, const char* value); + typedef void(BRIDGEAPI_PTR* PFN_bridgeapi_RegisterDevice)(void); + typedef void(__cdecl* PFN_bridgeapi_RegisterEndSceneCallback)(void); + + typedef struct bridgeapi_Interface { + bool initialized; + PFN_bridgeapi_DebugPrint DebugPrint; // const char* text + PFN_bridgeapi_CreateOpaqueMaterial CreateOpaqueMaterial; // x86::remixapi_MaterialInfo* + PFN_bridgeapi_CreateTranslucentMaterial CreateTranslucentMaterial; // x86::remixapi_MaterialInfo* --- x86::remixapi_MaterialInfoTranslucentEXT* + PFN_bridgeapi_CreatePortalMaterial CreatePortalMaterial; // x86::remixapi_MaterialInfo* --- x86::remixapi_MaterialInfoPortalEXT* + PFN_bridgeapi_DestroyMaterial DestroyMaterial; // uint64_t handle + PFN_bridgeapi_CreateTriangleMesh CreateTriangleMesh; // x86::remixapi_MeshInfo* + PFN_bridgeapi_DestroyMesh DestroyMesh; // uint64_t handle + PFN_bridgeapi_DrawMeshInstance DrawMeshInstance; // uint64_t handle --- x86::remixapi_Transform* t --- x86::remixapi_Bool double_sided + PFN_bridgeapi_CreateSphereLight CreateSphereLight; // x86::remixapi_LightInfo* --- x86::remixapi_LightInfoSphereEXT* + PFN_bridgeapi_CreateRectLight CreateRectLight; // x86::remixapi_LightInfo* --- x86::remixapi_LightInfoRectEXT* + PFN_bridgeapi_CreateDiskLight CreateDiskLight; // x86::remixapi_LightInfo* --- x86::remixapi_LightInfoDiskEXT* + PFN_bridgeapi_CreateCylinderLight CreateCylinderLight; // x86::remixapi_LightInfo* --- x86::remixapi_LightInfoCylinderEXT* + PFN_bridgeapi_CreateDistantLight CreateDistantLight; // x86::remixapi_LightInfo* --- x86::remixapi_LightInfoDistantEXT* + PFN_bridgeapi_DestroyLight DestroyLight; // uint64_t handle + PFN_bridgeapi_DrawLightInstance DrawLightInstance; // uint64_t handle + PFN_bridgeapi_SetConfigVariable SetConfigVariable; // const char* var --- const char* value + PFN_bridgeapi_RegisterDevice RegisterDevice; // void + void (*RegisterEndSceneCallback)(PFN_bridgeapi_RegisterEndSceneCallback callback); + } bridgeapi_Interface; + + BRIDGE_API BRIDGEAPI_ErrorCode __cdecl bridgeapi_InitFuncs(bridgeapi_Interface* out_result); + typedef BRIDGEAPI_ErrorCode(__cdecl* PFN_bridgeapi_InitFuncs)(bridgeapi_Interface* out_result); + + // -------- + // -------- + + inline BRIDGEAPI_ErrorCode bridgeapi_initialize(bridgeapi_Interface* out_bridgeInterface) { + + PFN_bridgeapi_InitFuncs pfn_Initialize = nullptr; + HMODULE hModule = GetModuleHandleA("d3d9.dll"); + if (hModule) { + PROC func = GetProcAddress(hModule, "bridgeapi_InitFuncs"); + if (func) { + pfn_Initialize = (PFN_bridgeapi_InitFuncs) func; + } + else { + return BRIDGEAPI_ERROR_CODE_GET_PROC_ADDRESS_FAILURE; + } + + bridgeapi_Interface bridgeInterface = { 0 }; + bridgeapi_ErrorCode status = pfn_Initialize(&bridgeInterface); + if (status != BRIDGEAPI_ERROR_CODE_SUCCESS) { + return status; + } + + bridgeInterface.initialized = true; + *out_bridgeInterface = bridgeInterface; + + return status; + } + return BRIDGEAPI_ERROR_CODE_GET_MODULE_HANDLE_FAILURE; + } + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // BRIDGE_C_H_ diff --git a/src/api/remix_api/remix_c.h b/src/api/remix_api/remix_c.h new file mode 100644 index 0000000..071f6f4 --- /dev/null +++ b/src/api/remix_api/remix_c.h @@ -0,0 +1,829 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. + * + * 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 REMIX_C_H_ +#define REMIX_C_H_ + +#include +#include + +#if _WIN64 != 1 + #error Remix API requires 64-bit for the ray tracing features. +#endif + + +// __stdcall convention +#define REMIXAPI_CALL __stdcall +#define REMIXAPI_PTR REMIXAPI_CALL + +#ifdef REMIX_LIBRARY_EXPORTS + #define REMIXAPI __declspec(dllexport) +#else + #define REMIXAPI __declspec(dllimport) +#endif // REMIX_LIBRARY_EXPORTS + + +#define REMIXAPI_VERSION_MAKE(major, minor, patch) ( \ + (((uint64_t)(major)) << 48) | \ + (((uint64_t)(minor)) << 16) | \ + (((uint64_t)(patch)) ) ) +#define REMIXAPI_VERSION_GET_MAJOR(version) (((uint64_t)(version) >> 48) & (uint64_t)0xFFFF) +#define REMIXAPI_VERSION_GET_MINOR(version) (((uint64_t)(version) >> 16) & (uint64_t)0xFFFFFFFF) +#define REMIXAPI_VERSION_GET_PATCH(version) (((uint64_t)(version) ) & (uint64_t)0xFFFF) + +#define REMIXAPI_VERSION_MAJOR 0 +#define REMIXAPI_VERSION_MINOR 4 +#define REMIXAPI_VERSION_PATCH 1 + + +// External +typedef struct IDirect3D9Ex IDirect3D9Ex; +typedef struct IDirect3DDevice9Ex IDirect3DDevice9Ex; +typedef struct IDirect3DSurface9 IDirect3DSurface9; + + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + typedef enum remixapi_StructType { + REMIXAPI_STRUCT_TYPE_NONE = 0, + REMIXAPI_STRUCT_TYPE_INITIALIZE_LIBRARY_INFO = 1, + REMIXAPI_STRUCT_TYPE_MATERIAL_INFO = 2, + REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_PORTAL_EXT = 3, + REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_TRANSLUCENT_EXT = 4, + REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_OPAQUE_EXT = 5, + REMIXAPI_STRUCT_TYPE_LIGHT_INFO = 6, + REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DISTANT_EXT = 7, + REMIXAPI_STRUCT_TYPE_LIGHT_INFO_CYLINDER_EXT = 8, + REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DISK_EXT = 9, + REMIXAPI_STRUCT_TYPE_LIGHT_INFO_RECT_EXT = 10, + REMIXAPI_STRUCT_TYPE_LIGHT_INFO_SPHERE_EXT = 11, + REMIXAPI_STRUCT_TYPE_MESH_INFO = 12, + REMIXAPI_STRUCT_TYPE_INSTANCE_INFO = 13, + REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_BONE_TRANSFORMS_EXT = 14, + REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_BLEND_EXT = 15, + REMIXAPI_STRUCT_TYPE_CAMERA_INFO = 16, + REMIXAPI_STRUCT_TYPE_CAMERA_INFO_PARAMETERIZED_EXT = 17, + REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_OPAQUE_SUBSURFACE_EXT = 18, + REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_OBJECT_PICKING_EXT = 19, + REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DOME_EXT = 20, + REMIXAPI_STRUCT_TYPE_LIGHT_INFO_USD_EXT = 21, + REMIXAPI_STRUCT_TYPE_STARTUP_INFO = 22, + REMIXAPI_STRUCT_TYPE_PRESENT_INFO = 23, + // NOTE: if adding a new struct, register it in 'rtx_remix_specialization.inl' + } remixapi_StructType; + + typedef enum remixapi_ErrorCode { + REMIXAPI_ERROR_CODE_SUCCESS = 0, + REMIXAPI_ERROR_CODE_GENERAL_FAILURE = 1, + // WinAPI's LoadLibrary has failed + REMIXAPI_ERROR_CODE_LOAD_LIBRARY_FAILURE = 2, + REMIXAPI_ERROR_CODE_INVALID_ARGUMENTS = 3, + // Couldn't find 'remixInitialize' function in the .dll + REMIXAPI_ERROR_CODE_GET_PROC_ADDRESS_FAILURE = 4, + // CreateD3D9 / RegisterD3D9Device can be called only once + REMIXAPI_ERROR_CODE_ALREADY_EXISTS = 5, + // RegisterD3D9Device requires the device that was created with IDirect3DDevice9Ex, returned by CreateD3D9 + REMIXAPI_ERROR_CODE_REGISTERING_NON_REMIX_D3D9_DEVICE = 6, + // RegisterD3D9Device was not called + REMIXAPI_ERROR_CODE_REMIX_DEVICE_WAS_NOT_REGISTERED = 7, + REMIXAPI_ERROR_CODE_INCOMPATIBLE_VERSION = 8, + // WinAPI's SetDllDirectory has failed + REMIXAPI_ERROR_CODE_SET_DLL_DIRECTORY_FAILURE = 9, + // WinAPI's GetFullPathName has failed + REMIXAPI_ERROR_CODE_GET_FULL_PATH_NAME_FAILURE = 10, + REMIXAPI_ERROR_CODE_NOT_INITIALIZED = 11, + // Error codes that are encoded as HRESULT, i.e. returned from D3D9 functions. + // Look MAKE_D3DHRESULT, but with _FACD3D=0x896, instead of D3D9's 0x876 + REMIXAPI_ERROR_CODE_HRESULT_NO_REQUIRED_GPU_FEATURES = 0x88960001, + REMIXAPI_ERROR_CODE_HRESULT_DRIVER_VERSION_BELOW_MINIMUM = 0x88960002, + REMIXAPI_ERROR_CODE_HRESULT_DXVK_INSTANCE_EXTENSION_FAIL = 0x88960003, + REMIXAPI_ERROR_CODE_HRESULT_VK_CREATE_INSTANCE_FAIL = 0x88960004, + REMIXAPI_ERROR_CODE_HRESULT_VK_CREATE_DEVICE_FAIL = 0x88960005, + REMIXAPI_ERROR_CODE_HRESULT_GRAPHICS_QUEUE_FAMILY_MISSING = 0x88960006, + } remixapi_ErrorCode; + + typedef uint32_t remixapi_Bool; + + typedef struct remixapi_Rect2D { + int32_t left; + int32_t top; + int32_t right; + int32_t bottom; + } remixapi_Rect2D; + + typedef struct remixapi_Float2D { + float x; + float y; + } remixapi_Float2D; + + typedef struct remixapi_Float3D { + float x; + float y; + float z; + } remixapi_Float3D; + + typedef struct remixapi_Float4D { + float x; + float y; + float z; + float w; + } remixapi_Float4D; + + typedef struct remixapi_Transform { + float matrix[3][4]; + } remixapi_Transform; + + typedef struct remixapi_MaterialHandle_T* remixapi_MaterialHandle; + typedef struct remixapi_MeshHandle_T* remixapi_MeshHandle; + typedef struct remixapi_LightHandle_T* remixapi_LightHandle; + + typedef const wchar_t* remixapi_Path; + + + + typedef struct remixapi_StartupInfo { + remixapi_StructType sType; + void* pNext; + HWND hwnd; + remixapi_Bool disableSrgbConversionForOutput; + // If true, 'dxvk_GetExternalSwapchain' can be used to retrieve a raw VkImage, + // so the application can present it, for example by using OpenGL interop: + // converting VkImage to OpenGL, and presenting it via OpenGL. + // Default: false. Use VkSwapchainKHR to present frame into HWND. + remixapi_Bool forceNoVkSwapchain; + remixapi_Bool editorModeEnabled; + } remixapi_StartupInfo; + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_Startup)(const remixapi_StartupInfo* info); + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_Shutdown)(void); + + + + typedef struct remixapi_MaterialInfoOpaqueEXT { + remixapi_StructType sType; + void* pNext; + remixapi_Path roughnessTexture; + remixapi_Path metallicTexture; + float anisotropy; + remixapi_Float3D albedoConstant; + float opacityConstant; + float roughnessConstant; + float metallicConstant; + remixapi_Bool thinFilmThickness_hasvalue; + float thinFilmThickness_value; + remixapi_Bool alphaIsThinFilmThickness; + remixapi_Path heightTexture; + float heightTextureStrength; + // If true, InstanceInfoBlendEXT is used as a source for alpha state + remixapi_Bool useDrawCallAlphaState; + remixapi_Bool blendType_hasvalue; + int blendType_value; + remixapi_Bool invertedBlend; + int alphaTestType; + uint8_t alphaReferenceValue; + } remixapi_MaterialInfoOpaqueEXT; + + // Valid only if remixapi_MaterialInfo contains remixapi_MaterialInfoOpaqueEXT in pNext chain + typedef struct remixapi_MaterialInfoOpaqueSubsurfaceEXT { + remixapi_StructType sType; + void* pNext; + remixapi_Path subsurfaceTransmittanceTexture; + remixapi_Path subsurfaceThicknessTexture; + remixapi_Path subsurfaceSingleScatteringAlbedoTexture; + remixapi_Float3D subsurfaceTransmittanceColor; + float subsurfaceMeasurementDistance; + remixapi_Float3D subsurfaceSingleScatteringAlbedo; + float subsurfaceVolumetricAnisotropy; + } remixapi_MaterialInfoOpaqueSubsurfaceEXT; + + typedef struct remixapi_MaterialInfoTranslucentEXT { + remixapi_StructType sType; + void* pNext; + remixapi_Path transmittanceTexture; + float refractiveIndex; + remixapi_Float3D transmittanceColor; + float transmittanceMeasurementDistance; + remixapi_Bool thinWallThickness_hasvalue; + float thinWallThickness_value; + remixapi_Bool useDiffuseLayer; + } remixapi_MaterialInfoTranslucentEXT; + + typedef struct remixapi_MaterialInfoPortalEXT { + remixapi_StructType sType; + void* pNext; + uint8_t rayPortalIndex; + float rotationSpeed; + } remixapi_MaterialInfoPortalEXT; + + typedef struct remixapi_MaterialInfo { + remixapi_StructType sType; + void* pNext; + uint64_t hash; + remixapi_Path albedoTexture; + remixapi_Path normalTexture; + remixapi_Path tangentTexture; + remixapi_Path emissiveTexture; + float emissiveIntensity; + remixapi_Float3D emissiveColorConstant; + uint8_t spriteSheetRow; + uint8_t spriteSheetCol; + uint8_t spriteSheetFps; + uint8_t filterMode; + uint8_t wrapModeU; + uint8_t wrapModeV; + } remixapi_MaterialInfo; + + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_CreateMaterial)( + const remixapi_MaterialInfo* info, + remixapi_MaterialHandle* out_handle); + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_DestroyMaterial)( + remixapi_MaterialHandle handle); + + typedef struct remixapi_HardcodedVertex { + float position[3]; + float normal[3]; + float texcoord[2]; + uint32_t color; + uint32_t _pad0; + uint32_t _pad1; + uint32_t _pad2; + uint32_t _pad3; + uint32_t _pad4; + uint32_t _pad5; + uint32_t _pad6; + } remixapi_HardcodedVertex; + + typedef struct remixapi_MeshInfoSkinning { + uint32_t bonesPerVertex; + // Each tuple of 'bonesPerVertex' float-s defines a vertex. + // I.e. the size must be (bonesPerVertex * vertexCount). + const float* blendWeights_values; + uint32_t blendWeights_count; + // Each tuple of 'bonesPerVertex' uint32_t-s defines a vertex. + // I.e. the size must be (bonesPerVertex * vertexCount). + const uint32_t* blendIndices_values; + uint32_t blendIndices_count; + } remixapi_MeshInfoSkinning; + + typedef struct remixapi_MeshInfoSurfaceTriangles { + const remixapi_HardcodedVertex* vertices_values; + uint64_t vertices_count; + const uint32_t* indices_values; + uint64_t indices_count; + remixapi_Bool skinning_hasvalue; + remixapi_MeshInfoSkinning skinning_value; + remixapi_MaterialHandle material; + } remixapi_MeshInfoSurfaceTriangles; + + typedef struct remixapi_MeshInfo { + remixapi_StructType sType; + void* pNext; + uint64_t hash; + const remixapi_MeshInfoSurfaceTriangles* surfaces_values; + uint32_t surfaces_count; + } remixapi_MeshInfo; + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_CreateMesh)( + const remixapi_MeshInfo* info, + remixapi_MeshHandle* out_handle); + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_DestroyMesh)( + remixapi_MeshHandle handle); + + + + typedef enum remixapi_CameraType { + REMIXAPI_CAMERA_TYPE_WORLD, + REMIXAPI_CAMERA_TYPE_SKY, + REMIXAPI_CAMERA_TYPE_VIEW_MODEL, + } remixapi_CameraType; + + typedef struct remixapi_CameraInfoParameterizedEXT { + remixapi_StructType sType; + void* pNext; + remixapi_Float3D position; + remixapi_Float3D forward; + remixapi_Float3D up; + remixapi_Float3D right; + float fovYInDegrees; + float aspect; + float nearPlane; + float farPlane; + } remixapi_CameraInfoParameterizedEXT; + + typedef struct remixapi_CameraInfo { + remixapi_StructType sType; + void* pNext; + remixapi_CameraType type; + float view[4][4]; + float projection[4][4]; + } remixapi_CameraInfo; + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_SetupCamera)( + const remixapi_CameraInfo* info); + + + +#define REMIXAPI_INSTANCE_INFO_MAX_BONES_COUNT 256 + + typedef struct remixapi_InstanceInfoBoneTransformsEXT { + remixapi_StructType sType; + void* pNext; + const remixapi_Transform* boneTransforms_values; + uint32_t boneTransforms_count; + } remixapi_InstanceInfoBoneTransformsEXT; + + typedef struct remixapi_InstanceInfoBlendEXT { + remixapi_StructType sType; + void* pNext; + remixapi_Bool alphaTestEnabled; + uint8_t alphaTestReferenceValue; + uint32_t alphaTestCompareOp; + remixapi_Bool alphaBlendEnabled; + uint32_t srcColorBlendFactor; + uint32_t dstColorBlendFactor; + uint32_t colorBlendOp; + uint32_t textureColorArg1Source; + uint32_t textureColorArg2Source; + uint32_t textureColorOperation; + uint32_t textureAlphaArg1Source; + uint32_t textureAlphaArg2Source; + uint32_t textureAlphaOperation; + uint32_t tFactor; + remixapi_Bool isTextureFactorBlend; + } remixapi_InstanceInfoBlendEXT; + + typedef struct remixapi_InstanceInfoObjectPickingEXT { + remixapi_StructType sType; + void* pNext; + // A value to write for 'RequestObjectPicking' + uint32_t objectPickingValue; + } remixapi_InstanceInfoObjectPickingEXT; + + typedef enum remixapi_InstanceCategoryBit { + REMIXAPI_INSTANCE_CATEGORY_BIT_WORLD_UI = 1 << 0, + REMIXAPI_INSTANCE_CATEGORY_BIT_WORLD_MATTE = 1 << 1, + REMIXAPI_INSTANCE_CATEGORY_BIT_SKY = 1 << 2, + REMIXAPI_INSTANCE_CATEGORY_BIT_IGNORE = 1 << 3, + REMIXAPI_INSTANCE_CATEGORY_BIT_IGNORE_LIGHTS = 1 << 4, + REMIXAPI_INSTANCE_CATEGORY_BIT_IGNORE_ANTI_CULLING = 1 << 5, + REMIXAPI_INSTANCE_CATEGORY_BIT_IGNORE_MOTION_BLUR = 1 << 6, + REMIXAPI_INSTANCE_CATEGORY_BIT_IGNORE_OPACITY_MICROMAP = 1 << 7, + REMIXAPI_INSTANCE_CATEGORY_BIT_HIDDEN = 1 << 8, + REMIXAPI_INSTANCE_CATEGORY_BIT_PARTICLE = 1 << 9, + REMIXAPI_INSTANCE_CATEGORY_BIT_BEAM = 1 << 10, + REMIXAPI_INSTANCE_CATEGORY_BIT_DECAL_STATIC = 1 << 11, + REMIXAPI_INSTANCE_CATEGORY_BIT_DECAL_DYNAMIC = 1 << 12, + REMIXAPI_INSTANCE_CATEGORY_BIT_DECAL_SINGLE_OFFSET = 1 << 13, + REMIXAPI_INSTANCE_CATEGORY_BIT_DECAL_NO_OFFSET = 1 << 14, + REMIXAPI_INSTANCE_CATEGORY_BIT_ALPHA_BLEND_TO_CUTOUT = 1 << 15, + REMIXAPI_INSTANCE_CATEGORY_BIT_TERRAIN = 1 << 16, + REMIXAPI_INSTANCE_CATEGORY_BIT_ANIMATED_WATER = 1 << 17, + REMIXAPI_INSTANCE_CATEGORY_BIT_THIRD_PERSON_PLAYER_MODEL = 1 << 18, + REMIXAPI_INSTANCE_CATEGORY_BIT_THIRD_PERSON_PLAYER_BODY = 1 << 19, + REMIXAPI_INSTANCE_CATEGORY_BIT_IGNORE_BAKED_LIGHTING = 1 << 20, + } remixapi_InstanceCategoryBit; + + typedef uint32_t remixapi_InstanceCategoryFlags; + + typedef struct remixapi_InstanceInfo { + remixapi_StructType sType; + void* pNext; + remixapi_InstanceCategoryFlags categoryFlags; + remixapi_MeshHandle mesh; + remixapi_Transform transform; + remixapi_Bool doubleSided; + } remixapi_InstanceInfo; + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_DrawInstance)( + const remixapi_InstanceInfo* info); + + + + typedef struct remixapi_LightInfoLightShaping { + // The direction the Light Shaping is pointing in. Must be normalized. + remixapi_Float3D direction; + float coneAngleDegrees; + float coneSoftness; + float focusExponent; + } remixapi_LightInfoLightShaping; + + typedef struct remixapi_LightInfoSphereEXT { + remixapi_StructType sType; + void* pNext; + remixapi_Float3D position; + float radius; + remixapi_Bool shaping_hasvalue; + remixapi_LightInfoLightShaping shaping_value; + } remixapi_LightInfoSphereEXT; + + typedef struct remixapi_LightInfoRectEXT { + remixapi_StructType sType; + void* pNext; + remixapi_Float3D position; + // The X axis of the Rect Light. Must be normalized and orthogonal to the Y and direction axes. + remixapi_Float3D xAxis; + float xSize; + // The Y axis of the Rect Light. Must be normalized and orthogonal to the X and direction axes. + remixapi_Float3D yAxis; + float ySize; + // The direction the Rect Light is pointing in, should match the Shaping direction if present. + // Must be normalized and orthogonal to the X and Y axes. + remixapi_Float3D direction; + remixapi_Bool shaping_hasvalue; + remixapi_LightInfoLightShaping shaping_value; + } remixapi_LightInfoRectEXT; + + typedef struct remixapi_LightInfoDiskEXT { + remixapi_StructType sType; + void* pNext; + remixapi_Float3D position; + // The X axis of the Disk Light. Must be normalized and orthogonal to the Y and direction axes. + remixapi_Float3D xAxis; + float xRadius; + // The Y axis of the Disk Light. Must be normalized and orthogonal to the X and direction axes. + remixapi_Float3D yAxis; + float yRadius; + // The direction the Disk Light is pointing in, should match the Shaping direction if present + // Must be normalized and orthogonal to the X and Y axes. + remixapi_Float3D direction; + remixapi_Bool shaping_hasvalue; + remixapi_LightInfoLightShaping shaping_value; + } remixapi_LightInfoDiskEXT; + + typedef struct remixapi_LightInfoCylinderEXT { + remixapi_StructType sType; + void* pNext; + remixapi_Float3D position; + float radius; + // The "center" axis of the Cylinder Light. Must be normalized. + remixapi_Float3D axis; + float axisLength; + } remixapi_LightInfoCylinderEXT; + + typedef struct remixapi_LightInfoDistantEXT { + remixapi_StructType sType; + void* pNext; + // The direction the Distant Light is pointing in. Must be normalized. + remixapi_Float3D direction; + float angularDiameterDegrees; + } remixapi_LightInfoDistantEXT; + + typedef struct remixapi_LightInfoDomeEXT { + remixapi_StructType sType; + void* pNext; + remixapi_Transform transform; + remixapi_Path colorTexture; + } remixapi_LightInfoDomeEXT; + + // Attachable to remixapi_LightInfo. + // If attached, 'remixapi_LightInfo::radiance' is ignored. + // Any other attached 'remixapi_LightInfo*EXT' are ignored. + // Most fields correspond to a usd token. Set to null, if no value. + typedef struct remixapi_LightInfoUSDEXT { + remixapi_StructType sType; + void* pNext; + remixapi_StructType lightType; + remixapi_Transform transform; + const float* pRadius; // "radius" + const float* pWidth; // "width" + const float* pHeight; // "height" + const float* pLength; // "length" + const float* pAngleRadians; // "angle" + const remixapi_Bool* pEnableColorTemp; // "enableColorTemperature" + const remixapi_Float3D* pColor; // "color" + const float* pColorTemp; // "colorTemperature" + const float* pExposure; // "exposure" + const float* pIntensity; // "intensity" + const float* pConeAngleRadians; // "shaping:cone:angle" + const float* pConeSoftness; // "shaping:cone:softness" + const float* pFocus; // "shaping:focus" + } remixapi_LightInfoUSDEXT; + + typedef struct remixapi_LightInfo { + remixapi_StructType sType; + void* pNext; + uint64_t hash; + remixapi_Float3D radiance; + } remixapi_LightInfo; + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_CreateLight)( + const remixapi_LightInfo* info, + remixapi_LightHandle* out_handle); + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_DestroyLight)( + remixapi_LightHandle handle); + + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_DrawLightInstance)( + remixapi_LightHandle lightHandle); + + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_SetConfigVariable)( + const char* key, + const char* value); + + typedef struct remixapi_PresentInfo { + remixapi_StructType sType; + void* pNext; + HWND hwndOverride; // Can be NULL + } remixapi_PresentInfo; + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_Present)(const remixapi_PresentInfo* info); + + typedef void (REMIXAPI_PTR* PFN_remixapi_pick_RequestObjectPickingUserCallback)( + const uint32_t* objectPickingValues_values, + uint32_t objectPickingValues_count, + void* callbackUserData); + + // Invokes 'callback' on a successful readback of 'remixapi_InstanceInfoObjectPickingEXT::objectPickingValue' + // of objects that are drawn in the 'pixelRegion'. 'pixelRegion' specified relative to the output size, + // not render size. 'callback' can be invoked from any thread. + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_pick_RequestObjectPicking)( + const remixapi_Rect2D* pixelRegion, + PFN_remixapi_pick_RequestObjectPickingUserCallback callback, + void* callbackUserData); + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_pick_HighlightObjects)( + const uint32_t* objectPickingValues_values, + uint32_t objectPickingValues_count, + uint8_t colorR, + uint8_t colorG, + uint8_t colorB); + + + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_dxvk_CreateD3D9)( + remixapi_Bool editorModeEnabled, + IDirect3D9Ex** out_pD3D9); + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_dxvk_RegisterD3D9Device)( + IDirect3DDevice9Ex* d3d9Device); + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_dxvk_GetExternalSwapchain)( + uint64_t* out_vkImage, + uint64_t* out_vkSemaphoreRenderingDone, + uint64_t* out_vkSemaphoreResumeSemaphore); + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_dxvk_GetVkImage)( + IDirect3DSurface9* source, + uint64_t* out_vkImage); + + typedef enum remixapi_dxvk_CopyRenderingOutputType { + REMIXAPI_DXVK_COPY_RENDERING_OUTPUT_TYPE_FINAL_COLOR = 0, + REMIXAPI_DXVK_COPY_RENDERING_OUTPUT_TYPE_DEPTH = 1, + REMIXAPI_DXVK_COPY_RENDERING_OUTPUT_TYPE_NORMALS = 2, + REMIXAPI_DXVK_COPY_RENDERING_OUTPUT_TYPE_OBJECT_PICKING = 3, + } remixapi_dxvk_CopyRenderingOutputType; + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_dxvk_CopyRenderingOutput)( + IDirect3DSurface9* destination, + remixapi_dxvk_CopyRenderingOutputType type); + + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_dxvk_SetDefaultOutput)( + remixapi_dxvk_CopyRenderingOutputType type, + const remixapi_Float4D* color); + + + typedef struct remixapi_InitializeLibraryInfo { + remixapi_StructType sType; + void* pNext; + uint64_t version; + } remixapi_InitializeLibraryInfo; + + typedef struct remixapi_Interface { + PFN_remixapi_Shutdown Shutdown; + PFN_remixapi_CreateMaterial CreateMaterial; + PFN_remixapi_DestroyMaterial DestroyMaterial; + PFN_remixapi_CreateMesh CreateMesh; + PFN_remixapi_DestroyMesh DestroyMesh; + PFN_remixapi_SetupCamera SetupCamera; + PFN_remixapi_DrawInstance DrawInstance; + PFN_remixapi_CreateLight CreateLight; + PFN_remixapi_DestroyLight DestroyLight; + PFN_remixapi_DrawLightInstance DrawLightInstance; + PFN_remixapi_SetConfigVariable SetConfigVariable; + + // DXVK interoperability + PFN_remixapi_dxvk_CreateD3D9 dxvk_CreateD3D9; + PFN_remixapi_dxvk_RegisterD3D9Device dxvk_RegisterD3D9Device; + PFN_remixapi_dxvk_GetExternalSwapchain dxvk_GetExternalSwapchain; + PFN_remixapi_dxvk_GetVkImage dxvk_GetVkImage; + PFN_remixapi_dxvk_CopyRenderingOutput dxvk_CopyRenderingOutput; + PFN_remixapi_dxvk_SetDefaultOutput dxvk_SetDefaultOutput; + // Object picking utils + PFN_remixapi_pick_RequestObjectPicking pick_RequestObjectPicking; + PFN_remixapi_pick_HighlightObjects pick_HighlightObjects; + + PFN_remixapi_Startup Startup; + PFN_remixapi_Present Present; + } remixapi_Interface; + + REMIXAPI remixapi_ErrorCode REMIXAPI_CALL remixapi_InitializeLibrary( + const remixapi_InitializeLibraryInfo* info, + remixapi_Interface* out_result); + + typedef remixapi_ErrorCode(REMIXAPI_CALL* PFN_remixapi_InitializeLibrary)( + const remixapi_InitializeLibraryInfo* info, + remixapi_Interface* out_result); + + + inline remixapi_ErrorCode REMIXAPI_CALL remixapi_lib_loadRemixDllAndInitialize( + const wchar_t* remixD3D9DllPath, + remixapi_Interface* out_remixInterface, + HMODULE* out_remixDll + ) { + if (remixD3D9DllPath == NULL || remixD3D9DllPath[0] == '\0') { + return REMIXAPI_ERROR_CODE_INVALID_ARGUMENTS; + } + if (out_remixInterface == NULL || out_remixDll == NULL) { + return REMIXAPI_ERROR_CODE_INVALID_ARGUMENTS; + } + + HMODULE remixDll = NULL; + PFN_remixapi_InitializeLibrary pfn_InitializeLibrary = NULL; + { + // firstly, try the default method first, e.g. DLL is already loaded, + // DLL-s are around .exe, or an app has called SetDllDirectory + { + HMODULE dll = LoadLibraryW(remixD3D9DllPath); + if (dll) { + PROC func = GetProcAddress(dll, "remixapi_InitializeLibrary"); + if (func) { + remixDll = dll; + pfn_InitializeLibrary = (PFN_remixapi_InitializeLibrary) func; + } else { + FreeLibrary(dll); + } + } + } + + // then try raw user-provided 'remixD3D9DllPath' file + if (!pfn_InitializeLibrary) { + // set LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR to search + // dependency DLL-s in the folder of 'remixD3D9DllPath' + HMODULE dll = LoadLibraryExW(remixD3D9DllPath, NULL, + LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | + LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); + if (dll) { + PROC func = GetProcAddress(dll, "remixapi_InitializeLibrary"); + if (func) { + remixDll = dll; + pfn_InitializeLibrary = (PFN_remixapi_InitializeLibrary) func; + } else { + FreeLibrary(dll); + } + } + } + + // at last, try to SetDllDirectory manually + if (!pfn_InitializeLibrary) { + wchar_t absoluteD3D9DllPath[MAX_PATH]; + { + DWORD ret = GetFullPathNameW(remixD3D9DllPath, MAX_PATH, absoluteD3D9DllPath, NULL); + if (ret == 0) { + return REMIXAPI_ERROR_CODE_GET_FULL_PATH_NAME_FAILURE; + } + } + wchar_t parentDir[MAX_PATH]; + { + int len = 0; + for (int i = 0; i < MAX_PATH; i++) { + if (absoluteD3D9DllPath[i] == '\0') { + break; + } + parentDir[i] = absoluteD3D9DllPath[i] == '/' ? '\\' : absoluteD3D9DllPath[i]; + ++len; + } + if (len <= 0 || len >= MAX_PATH) { + return REMIXAPI_ERROR_CODE_INVALID_ARGUMENTS; + } + parentDir[len] = '\0'; + for (int i = len - 1; i >= 0; --i) { + if (parentDir[i] == '\\') { + // remove one or more path separators + for (int k = i; k >= 0; --k) { + if (parentDir[k] == '\\') { + parentDir[k] = '\0'; + continue; + } + break; + } + break; + } + } + if (parentDir[0] == '\0') { + return REMIXAPI_ERROR_CODE_INVALID_ARGUMENTS; + } + } + + // save the previous value that is in SetDllDirectory + wchar_t dirToRestore[MAX_PATH]; + { + DWORD len = GetDllDirectoryW(MAX_PATH, dirToRestore); + if (len > 0 && len < MAX_PATH - 1) { + dirToRestore[MAX_PATH - 1] = '\0'; + } + } + + { + BOOL s = SetDllDirectoryW(parentDir); + if (!s) { + return REMIXAPI_ERROR_CODE_SET_DLL_DIRECTORY_FAILURE; + } + } + + HMODULE dll = LoadLibraryW(absoluteD3D9DllPath); + if (dll) { + PROC func = GetProcAddress(dll, "remixapi_InitializeLibrary"); + if (func) { + remixDll = dll; + pfn_InitializeLibrary = (PFN_remixapi_InitializeLibrary) func; + } else { + FreeLibrary(dll); + } + } + + // restore the previous value + { + SetDllDirectoryW(dirToRestore); + } + } + } + + if (!remixDll) { + return REMIXAPI_ERROR_CODE_LOAD_LIBRARY_FAILURE; + } + if (!pfn_InitializeLibrary){ + return REMIXAPI_ERROR_CODE_GET_PROC_ADDRESS_FAILURE; + } + + remixapi_InitializeLibraryInfo info = {}; + { + info.sType = REMIXAPI_STRUCT_TYPE_INITIALIZE_LIBRARY_INFO; + info.version = REMIXAPI_VERSION_MAKE(REMIXAPI_VERSION_MAJOR, + REMIXAPI_VERSION_MINOR, + REMIXAPI_VERSION_PATCH); + } + remixapi_Interface remixInterface = { nullptr }; + + remixapi_ErrorCode status = pfn_InitializeLibrary(&info, &remixInterface); + if (status != REMIXAPI_ERROR_CODE_SUCCESS) { + FreeLibrary(remixDll); + return status; + } + + *out_remixInterface = remixInterface; + *out_remixDll = remixDll; + return status; + } + + inline remixapi_ErrorCode REMIXAPI_CALL remixapi_lib_shutdownAndUnloadRemixDll( + remixapi_Interface* remixInterface, + HMODULE remixDll + ) { + if (remixInterface == NULL || remixInterface->Shutdown == NULL) { + if (remixDll != NULL) { + FreeLibrary(remixDll); + } + return REMIXAPI_ERROR_CODE_INVALID_ARGUMENTS; + } + + remixapi_ErrorCode status = remixInterface->Shutdown(); + if (remixDll != NULL) { + FreeLibrary(remixDll); + } + + remixapi_Interface nullInterface = { 0 }; + *remixInterface = nullInterface; + return status; + } + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // REMIX_C_H_ diff --git a/src/client/client_options.h b/src/client/client_options.h index ec2146c..551b2b0 100644 --- a/src/client/client_options.h +++ b/src/client/client_options.h @@ -67,6 +67,10 @@ namespace ClientOptions { return bridge_util::Config::getOption("client.enableDpiAwareness", true); } + inline bool getExposeRemixApi() { + return bridge_util::Config::getOption("client.exposeRemixApi", false); + } + // If set, the space for data for dynamic buffer updates will be preallocated on data channel // and redundant copy will be avioded. However, because D3D applications are not obliged // to write the entire locked region this optimization is NOT considered safe and may diff --git a/src/client/d3d9_device.cpp b/src/client/d3d9_device.cpp index c705c70..d7a54b1 100644 --- a/src/client/d3d9_device.cpp +++ b/src/client/d3d9_device.cpp @@ -46,6 +46,8 @@ #include #include + #include "remix_api.h" + #define GET_PRES_PARAM() (m_pSwapchain->getPresentationParameters()) #define SetShaderConst(func, StartRegister, pConstantData, Count, size, currentUID) \ @@ -1117,6 +1119,10 @@ HRESULT Direct3DDevice9Ex_LSS::EndScene() { } } + if (remix_api::interfaceInitialized && remix_api::interfaceGameCallback) { + remix_api::interfaceGameCallback(); + } + UID currentUID = 0; { ClientMessage c(Commands::IDirect3DDevice9Ex_EndScene, getId()); diff --git a/src/client/meson.build b/src/client/meson.build index 8fe45ac..e1bf496 100644 --- a/src/client/meson.build +++ b/src/client/meson.build @@ -49,6 +49,7 @@ d3d9_src = files([ 'd3d9_volumetexture.cpp', 'di_hook.cpp', 'pch.cpp', + 'remix_api.cpp', 'remix_state.cpp', ]) @@ -81,6 +82,7 @@ d3d9_header = files([ 'framework.h', 'lockable_buffer.h', 'pch.h', + 'remix_api.h', 'remix_state.h', 'resource.h', 'shadow_map.h', @@ -91,13 +93,14 @@ d3d9_def = files([ ]) detours_include_path = include_directories('../../ext/Detours/src') +remixapi_include_path = include_directories('../api') thread_dep = dependency('threads') d3d9_dll = shared_library('d3d9', d3d9_src, d3d9_res, d3d9_header, sources : [ bridge_version ], dependencies : [ thread_dep, util_dep, lib_version, lib_version, lib_commCtrl, tracy_dep ], - include_directories : [ bridge_include_path, util_include_path, detours_include_path ], + include_directories : [ bridge_include_path, util_include_path, detours_include_path, remixapi_include_path ], install : true, vs_module_defs : 'd3d9_lss.def') diff --git a/src/client/remix_api.cpp b/src/client/remix_api.cpp new file mode 100644 index 0000000..9ded96c --- /dev/null +++ b/src/client/remix_api.cpp @@ -0,0 +1,507 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. + * + * 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 "log/log.h" +#include "util_bridgecommand.h" +#include "util_devicecommand.h" +#include "remix_api.h" + + +#define SEND_FLOAT(MSG, V) \ + (MSG).send_data((uint32_t) *(uint32_t*)(&(V))) + +#define SEND_FLOAT2D(MSG, V) \ + SEND_FLOAT(MSG, (V).x); \ + SEND_FLOAT(MSG, (V).y) + +#define SEND_FLOAT3D(MSG, V) \ + SEND_FLOAT(MSG, (V).x); \ + SEND_FLOAT(MSG, (V).y); \ + SEND_FLOAT(MSG, (V).z) + +#define SEND_FLOAT4D(MSG, V) \ + SEND_FLOAT(MSG, (V).x); \ + SEND_FLOAT(MSG, (V).y); \ + SEND_FLOAT(MSG, (V).z); \ + SEND_FLOAT(MSG, (V).w) + +#define SEND_INT(MSG, T) \ + (MSG).send_data((int32_t) (T)) + +#define SEND_STYPE(MSG, T) \ + (MSG).send_data((uint32_t) (T)) + +#define SEND_U32(MSG, U32) \ + (MSG).send_data((uint32_t) (U32)) + +#define SEND_U64(MSG, U64) \ + (MSG).send_data(sizeof(uint64_t), &(U64)) + +#define SEND_PATH(MSG, PATH) \ + (MSG).send_data((uint32_t)wcslen((PATH) ? (PATH) : L"") * sizeof(wchar_t), (PATH) ? (PATH) : L"") + +#define PULL_DATA(SIZE, NAME) \ + uint32_t NAME##_len = DeviceBridge::get_data((void**)&(NAME)); \ + assert(NAME##_len == 0 || (SIZE) == NAME##_len) + +namespace remix_api { + bool interfaceInitialized = false; + PFN_bridgeapi_RegisterEndSceneCallback interfaceGameCallback = nullptr; +} + +namespace { + void BRIDGEAPI_CALL bridgeapi_DebugPrint(const char* text) { + if (text) { + ClientMessage c(Commands::Api_DebugPrint); + c.send_data((uint32_t) strlen(text), text); + } + } + + uint64_t BRIDGEAPI_CALL bridgeapi_CreateOpaqueMaterial(const x86::remixapi_MaterialInfo* info, const x86::remixapi_MaterialInfoOpaqueEXT* ext, const x86::remixapi_MaterialInfoOpaqueSubsurfaceEXT* ext_ss) { + UID currentUID = 0; + { + ClientMessage c(Commands::Api_CreateOpaqueMaterial); + currentUID = c.get_uid(); + + // MaterialInfo + SEND_STYPE(c, info->sType); + SEND_U64(c, info->hash); + SEND_PATH(c, info->albedoTexture); + SEND_PATH(c, info->normalTexture); + SEND_PATH(c, info->tangentTexture); + SEND_PATH(c, info->emissiveTexture); + SEND_FLOAT(c, info->emissiveIntensity); + SEND_FLOAT3D(c, info->emissiveColorConstant); + c.send_data((uint8_t) info->spriteSheetRow); + c.send_data((uint8_t) info->spriteSheetCol); + c.send_data((uint8_t) info->spriteSheetFps); + c.send_data((uint8_t) info->filterMode); + c.send_data((uint8_t) info->wrapModeU); + c.send_data((uint8_t) info->wrapModeV); + + // MaterialInfoOpaqueEXT + SEND_STYPE(c, ext->sType); + SEND_PATH(c, ext->roughnessTexture); + SEND_PATH(c, ext->metallicTexture); + SEND_FLOAT(c, ext->anisotropy); + SEND_FLOAT3D(c, ext->albedoConstant); + SEND_FLOAT(c, ext->opacityConstant); + SEND_FLOAT(c, ext->roughnessConstant); + SEND_FLOAT(c, ext->metallicConstant); + SEND_U32(c, ext->thinFilmThickness_hasvalue); + SEND_FLOAT(c, ext->thinFilmThickness_value); + SEND_U32(c, ext->alphaIsThinFilmThickness); + SEND_PATH(c, ext->heightTexture); + SEND_FLOAT(c, ext->heightTextureStrength); + SEND_U32(c, ext->useDrawCallAlphaState); // If true, InstanceInfoBlendEXT is used as a source for alpha state + SEND_U32(c, ext->blendType_hasvalue); + SEND_INT(c, ext->blendType_value); + SEND_U32(c, ext->invertedBlend); + SEND_INT(c, ext->alphaTestType); + c.send_data((uint8_t) ext->alphaReferenceValue); + + const x86::remixapi_Bool has_ss = ext_ss ? TRUE : FALSE; + SEND_U32(c, has_ss); + + if (ext_ss) { + // MaterialInfoOpaqueSubsurfaceEXT + SEND_STYPE(c, ext_ss->sType); + SEND_PATH(c, ext_ss->subsurfaceTransmittanceTexture); + SEND_PATH(c, ext_ss->subsurfaceThicknessTexture); + SEND_PATH(c, ext_ss->subsurfaceSingleScatteringAlbedoTexture); + SEND_FLOAT3D(c, ext_ss->subsurfaceTransmittanceColor); + SEND_FLOAT(c, ext_ss->subsurfaceMeasurementDistance); + SEND_FLOAT3D(c, ext_ss->subsurfaceSingleScatteringAlbedo); + SEND_FLOAT(c, ext_ss->subsurfaceVolumetricAnisotropy); + } + } + + WAIT_FOR_SERVER_RESPONSE("CreateMaterial()", 0, currentUID); + uint64_t* result = nullptr; + PULL_DATA(sizeof(uint64_t), result); + DeviceBridge::pop_front(); + return *result; + } + + uint64_t BRIDGEAPI_CALL bridgeapi_CreateTranslucentMaterial(const x86::remixapi_MaterialInfo* info, const x86::remixapi_MaterialInfoTranslucentEXT* ext) { + UID currentUID = 0; + { + ClientMessage c(Commands::Api_CreateTranslucentMaterial); + currentUID = c.get_uid(); + + // MaterialInfo + SEND_STYPE(c, info->sType); + SEND_U64(c, info->hash); + SEND_PATH(c, info->albedoTexture); + SEND_PATH(c, info->normalTexture); + SEND_PATH(c, info->tangentTexture); + SEND_PATH(c, info->emissiveTexture); + SEND_FLOAT(c, info->emissiveIntensity); + SEND_FLOAT3D(c, info->emissiveColorConstant); + c.send_data((uint8_t) info->spriteSheetRow); + c.send_data((uint8_t) info->spriteSheetCol); + c.send_data((uint8_t) info->spriteSheetFps); + c.send_data((uint8_t) info->filterMode); + c.send_data((uint8_t) info->wrapModeU); + c.send_data((uint8_t) info->wrapModeV); + + // MaterialInfoTranslucentEXT + SEND_STYPE(c, ext->sType); + SEND_PATH(c, ext->transmittanceTexture); + SEND_FLOAT(c, ext->refractiveIndex); + SEND_FLOAT3D(c, ext->transmittanceColor); + SEND_FLOAT(c, ext->transmittanceMeasurementDistance); + SEND_U32(c, ext->thinWallThickness_hasvalue); + SEND_FLOAT(c, ext->thinWallThickness_value); + SEND_U32(c, ext->useDiffuseLayer); + } + + WAIT_FOR_SERVER_RESPONSE("CreateMaterial()", 0, currentUID); + uint64_t* result = nullptr; + PULL_DATA(sizeof(uint64_t), result); + DeviceBridge::pop_front(); + return *result; + } + + uint64_t BRIDGEAPI_CALL bridgeapi_CreatePortalMaterial(const x86::remixapi_MaterialInfo* info, const x86::remixapi_MaterialInfoPortalEXT* ext) { + UID currentUID = 0; + { + ClientMessage c(Commands::Api_CreatePortalMaterial); + currentUID = c.get_uid(); + + // MaterialInfo + SEND_STYPE(c, info->sType); + SEND_U64(c, info->hash); + SEND_PATH(c, info->albedoTexture); + SEND_PATH(c, info->normalTexture); + SEND_PATH(c, info->tangentTexture); + SEND_PATH(c, info->emissiveTexture); + SEND_FLOAT(c, info->emissiveIntensity); + SEND_FLOAT3D(c, info->emissiveColorConstant); + c.send_data((uint8_t) info->spriteSheetRow); + c.send_data((uint8_t) info->spriteSheetCol); + c.send_data((uint8_t) info->spriteSheetFps); + c.send_data((uint8_t) info->filterMode); + c.send_data((uint8_t) info->wrapModeU); + c.send_data((uint8_t) info->wrapModeV); + + // MaterialInfoPortalEXT + SEND_STYPE(c, ext->sType); + c.send_data((uint8_t) ext->rayPortalIndex); + SEND_FLOAT(c, ext->rotationSpeed); + } + + WAIT_FOR_SERVER_RESPONSE("CreateMaterial()", 0, currentUID); + uint64_t* result = nullptr; + PULL_DATA(sizeof(uint64_t), result); + DeviceBridge::pop_front(); + return *result; + } + + void BRIDGEAPI_CALL bridgeapi_DestroyMaterial(uint64_t handle) { + ClientMessage c(Commands::Api_DestroyMaterial); + SEND_U64(c, handle); + } + + uint64_t BRIDGEAPI_CALL bridgeapi_CreateTriangleMesh(const x86::remixapi_MeshInfo* info) { + UID currentUID = 0; + { + ClientMessage c(Commands::Api_CreateTriangleMesh); + currentUID = c.get_uid(); + + // MeshInfo + SEND_STYPE(c, info->sType); + SEND_U64(c, info->hash); + + // send each surface + SEND_U32(c, info->surfaces_count); // send surface count before sending the surfaces + for (uint32_t s = 0u; s < info->surfaces_count; s++) + { + const auto& surf = info->surfaces_values[s]; + + // send vertices of the current surface + SEND_U64(c, surf.vertices_count); // send vertex count before vertices + for (uint64_t v = 0u; v < surf.vertices_count; v++) + { + const auto& vert = surf.vertices_values[v]; + SEND_FLOAT(c, vert.position[0]); + SEND_FLOAT(c, vert.position[1]); + SEND_FLOAT(c, vert.position[2]); + SEND_FLOAT(c, vert.normal[0]); + SEND_FLOAT(c, vert.normal[1]); + SEND_FLOAT(c, vert.normal[2]); + SEND_FLOAT(c, vert.texcoord[0]); + SEND_FLOAT(c, vert.texcoord[1]); + SEND_U32(c, vert.color); + } + + // send indices of the current surface + SEND_U64(c, surf.indices_count); // send index count before indices + for (uint64_t i = 0u; i < surf.indices_count; i++) { + SEND_U32(c, surf.indices_values[i]); + } + + SEND_U32(c, surf.skinning_hasvalue); + // # TODO skinning + + // using remixapi_MaterialHandle is unpractical and kinda unsafe because its only 4 bytes (ptr) + // so user would have to send an actual pointer instead of the uint64_t hash val + SEND_U64(c, surf.material); + } + } + + WAIT_FOR_SERVER_RESPONSE("CreateMesh()", 0, currentUID); + uint64_t* result = nullptr; + PULL_DATA(sizeof(uint64_t), result); + DeviceBridge::pop_front(); + return *result; + } + + void BRIDGEAPI_CALL bridgeapi_DestroyMesh(uint64_t handle) { + ClientMessage c(Commands::Api_DestroyMesh); + SEND_U64(c, handle); + } + + void BRIDGEAPI_CALL bridgeapi_DrawMeshInstance(uint64_t handle, const x86::remixapi_Transform* t, x86::remixapi_Bool double_sided) { + ClientMessage c(Commands::Api_DrawMeshInstance); + SEND_U64(c, handle); + SEND_FLOAT(c, t->matrix[0][0]); SEND_FLOAT(c, t->matrix[0][1]); SEND_FLOAT(c, t->matrix[0][2]); SEND_FLOAT(c, t->matrix[0][3]); + SEND_FLOAT(c, t->matrix[1][0]); SEND_FLOAT(c, t->matrix[1][1]); SEND_FLOAT(c, t->matrix[1][2]); SEND_FLOAT(c, t->matrix[1][3]); + SEND_FLOAT(c, t->matrix[2][0]); SEND_FLOAT(c, t->matrix[2][1]); SEND_FLOAT(c, t->matrix[2][2]); SEND_FLOAT(c, t->matrix[2][3]); + SEND_U32(c, double_sided); + } + + uint64_t BRIDGEAPI_CALL bridgeapi_CreateSphereLight(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoSphereEXT* ext) { + UID currentUID = 0; + { + ClientMessage c(Commands::Api_CreateSphereLight); + currentUID = c.get_uid(); + + // LightInfo + SEND_STYPE(c, info->sType); + SEND_U64(c, info->hash); + SEND_FLOAT3D(c, info->radiance); + + // LightInfoSphereEXT + SEND_STYPE(c, ext->sType); + SEND_FLOAT3D(c, ext->position); + SEND_FLOAT(c, ext->radius); + SEND_U32(c, ext->shaping_hasvalue); + + if (ext->shaping_hasvalue) { + SEND_FLOAT3D(c, ext->shaping_value.direction); + SEND_FLOAT(c, ext->shaping_value.coneAngleDegrees); + SEND_FLOAT(c, ext->shaping_value.coneSoftness); + SEND_FLOAT(c, ext->shaping_value.focusExponent); + } + } + + WAIT_FOR_SERVER_RESPONSE("CreateLight()", 0, currentUID); + uint64_t* result = nullptr; + PULL_DATA(sizeof(uint64_t), result); + DeviceBridge::pop_front(); + return *result; + } + + uint64_t BRIDGEAPI_CALL bridgeapi_CreateRectLight(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoRectEXT* ext) { + UID currentUID = 0; + { + ClientMessage c(Commands::Api_CreateRectLight); + currentUID = c.get_uid(); + + // LightInfo + SEND_STYPE(c, info->sType); + SEND_U64(c, info->hash); + SEND_FLOAT3D(c, info->radiance); + + // LightInfoRectEXT + SEND_STYPE(c, ext->sType); + SEND_FLOAT3D(c, ext->position); + SEND_FLOAT3D(c, ext->xAxis); + SEND_FLOAT(c, ext->xSize); + SEND_FLOAT3D(c, ext->yAxis); + SEND_FLOAT(c, ext->ySize); + SEND_FLOAT3D(c, ext->direction); + SEND_U32(c, ext->shaping_hasvalue); + + if (ext->shaping_hasvalue) { + SEND_FLOAT3D(c, ext->shaping_value.direction); + SEND_FLOAT(c, ext->shaping_value.coneAngleDegrees); + SEND_FLOAT(c, ext->shaping_value.coneSoftness); + SEND_FLOAT(c, ext->shaping_value.focusExponent); + } + } + + WAIT_FOR_SERVER_RESPONSE("CreateLight()", 0, currentUID); + uint64_t* result = nullptr; + PULL_DATA(sizeof(uint64_t), result); + DeviceBridge::pop_front(); + return *result; + } + + uint64_t BRIDGEAPI_CALL bridgeapi_CreateDiscLight(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoDiskEXT* ext) { + UID currentUID = 0; + { + ClientMessage c(Commands::Api_CreateDiskLight); + currentUID = c.get_uid(); + + // LightInfo + SEND_STYPE(c, info->sType); + SEND_U64(c, info->hash); + SEND_FLOAT3D(c, info->radiance); + + // LightInfoDiskEXT + SEND_STYPE(c, ext->sType); + SEND_FLOAT3D(c, ext->position); + SEND_FLOAT3D(c, ext->xAxis); + SEND_FLOAT(c, ext->xRadius); + SEND_FLOAT3D(c, ext->yAxis); + SEND_FLOAT(c, ext->yRadius); + SEND_FLOAT3D(c, ext->direction); + SEND_U32(c, ext->shaping_hasvalue); + + if (ext->shaping_hasvalue) { + SEND_FLOAT3D(c, ext->shaping_value.direction); + SEND_FLOAT(c, ext->shaping_value.coneAngleDegrees); + SEND_FLOAT(c, ext->shaping_value.coneSoftness); + SEND_FLOAT(c, ext->shaping_value.focusExponent); + } + } + + WAIT_FOR_SERVER_RESPONSE("CreateLight()", 0, currentUID); + uint64_t* result = nullptr; + PULL_DATA(sizeof(uint64_t), result); + DeviceBridge::pop_front(); + return *result; + } + + uint64_t BRIDGEAPI_CALL bridgeapi_CreateCylinderLight(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoCylinderEXT* ext) { + UID currentUID = 0; + { + ClientMessage c(Commands::Api_CreateCylinderLight); + currentUID = c.get_uid(); + + // LightInfo + SEND_STYPE(c, info->sType); + SEND_U64(c, info->hash); + SEND_FLOAT3D(c, info->radiance); + + // LightInfoCylinderEXT + SEND_STYPE(c, ext->sType); + SEND_FLOAT3D(c, ext->position); + SEND_FLOAT(c, ext->radius); + SEND_FLOAT3D(c, ext->axis); + SEND_FLOAT(c, ext->axisLength); + } + + WAIT_FOR_SERVER_RESPONSE("CreateLight()", 0, currentUID); + uint64_t* result = nullptr; + PULL_DATA(sizeof(uint64_t), result); + DeviceBridge::pop_front(); + return *result; + } + + uint64_t BRIDGEAPI_CALL bridgeapi_CreateDistantLight(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoDistantEXT* ext) { + UID currentUID = 0; + { + ClientMessage c(Commands::Api_CreateDistantLight); + currentUID = c.get_uid(); + + // LightInfo + SEND_STYPE(c, info->sType); + SEND_U64(c, info->hash); + SEND_FLOAT3D(c, info->radiance); + + // LightInfoDistantEXT + SEND_STYPE(c, ext->sType); + SEND_FLOAT3D(c, ext->direction); + SEND_FLOAT(c, ext->angularDiameterDegrees); + } + + WAIT_FOR_SERVER_RESPONSE("CreateLight()", 0, currentUID); + uint64_t* result = nullptr; + PULL_DATA(sizeof(uint64_t), result); + DeviceBridge::pop_front(); + return *result; + } + + void BRIDGEAPI_CALL bridgeapi_DestroyLight(uint64_t handle) { + ClientMessage c(Commands::Api_DestroyLight); + SEND_U64(c, handle); + } + + void BRIDGEAPI_CALL bridgeapi_DrawLightInstance(uint64_t handle) { + ClientMessage c(Commands::Api_DrawLightInstance); + SEND_U64(c, handle); + } + + void BRIDGEAPI_CALL bridgeapi_SetConfigVariable(const char* var, const char* value) { + if (var && value) + { + ClientMessage c(Commands::Api_SetConfigVariable); + c.send_data((uint32_t) strlen(var), var); + c.send_data((uint32_t) strlen(value), value); + } + } + + void BRIDGEAPI_CALL bridgeapi_RegisterDevice() { + ClientMessage c(Commands::Api_RegisterDevice); + } + + BRIDGE_API void bridgeapi_RegisterEndSceneCallback(PFN_bridgeapi_RegisterEndSceneCallback callback) { + remix_api::interfaceGameCallback = callback; + } + + extern "C" { + BRIDGE_API BRIDGEAPI_ErrorCode __cdecl bridgeapi_InitFuncs(bridgeapi_Interface* out_result) { + if (!out_result) { + return BRIDGEAPI_ERROR_CODE_INVALID_ARGUMENTS; + } + auto interf = bridgeapi_Interface {}; + { + interf.DebugPrint = bridgeapi_DebugPrint; + interf.CreateOpaqueMaterial = bridgeapi_CreateOpaqueMaterial; + interf.CreateTranslucentMaterial = bridgeapi_CreateTranslucentMaterial; + interf.CreatePortalMaterial = bridgeapi_CreatePortalMaterial; + interf.DestroyMaterial = bridgeapi_DestroyMaterial; + interf.CreateTriangleMesh = bridgeapi_CreateTriangleMesh; + interf.DestroyMesh = bridgeapi_DestroyMesh; + interf.DrawMeshInstance = bridgeapi_DrawMeshInstance; + interf.CreateSphereLight = bridgeapi_CreateSphereLight; + interf.CreateRectLight = bridgeapi_CreateRectLight; + interf.CreateDiskLight = bridgeapi_CreateDiscLight; + interf.CreateCylinderLight = bridgeapi_CreateCylinderLight; + interf.CreateDistantLight = bridgeapi_CreateDistantLight; + interf.DestroyLight = bridgeapi_DestroyLight; + interf.DrawLightInstance = bridgeapi_DrawLightInstance; + interf.SetConfigVariable = bridgeapi_SetConfigVariable; + interf.RegisterDevice = bridgeapi_RegisterDevice; + interf.RegisterEndSceneCallback = bridgeapi_RegisterEndSceneCallback; + } + + *out_result = interf; + remix_api::interfaceInitialized = true; + + return BRIDGEAPI_ERROR_CODE_SUCCESS; + } + } +} diff --git a/src/client/remix_api.h b/src/client/remix_api.h new file mode 100644 index 0000000..61e4cce --- /dev/null +++ b/src/client/remix_api.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ +#pragma once + +#include "remix_api/bridge_c.h" + +namespace remix_api { + extern bool interfaceInitialized; + extern PFN_bridgeapi_RegisterEndSceneCallback interfaceGameCallback; +} diff --git a/src/server/main.cpp b/src/server/main.cpp index 54c7bca..6326aa8 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -54,6 +54,9 @@ #include #include +#include "remix_api/remix_c.h" +#include "../client/client_options.h" + using namespace Commands; using namespace bridge_util; @@ -99,6 +102,37 @@ using namespace bridge_util; const auto& name = map[name##Handle]; \ assert(name != NULL) +namespace { + remixapi_StructType pullSType() { + return (remixapi_StructType) DeviceBridge::get_data(); + } + + int32_t pullInt() { + return (int32_t) DeviceBridge::get_data(); + } + + uint32_t pullUInt32() { + return (uint32_t) DeviceBridge::get_data(); + } + + uint64_t pullUInt64() { + uint64_t* r = nullptr; + uint32_t s = DeviceBridge::get_data((void**) &r); + assert(s == 0 || sizeof(uint64_t) == s); + return *r; + } + + std::wstring pullPath() { + wchar_t* t = nullptr; + const uint32_t len = DeviceBridge::getReaderChannel().data->pull((void**) &t) / sizeof(wchar_t); + return std::wstring(t, len); + } + + float pullFloat() { + return *(float*)&DeviceBridge::get_data(); + } +} + // NOTE: MSDN states HWNDs are safe to cross x86-->x64 boundary, and that a truncating cast should be used: // https://docs.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication?redirectedfrom=MSDN #define TRUNCATE_HANDLE(type, input) (type)(size_t)(input) @@ -2675,6 +2709,515 @@ void ProcessDeviceCommandQueue() { gpD3DResources.erase(pHandle); break; } + + /* + * BridgeApi commands + */ + case Api_DebugPrint: + { + void* text_ptr = nullptr; + const uint32_t text_size = DeviceBridge::getReaderChannel().data->pull(&text_ptr); + Logger::info(std::string((const char*) text_ptr, text_size)); + break; + } + + case Api_CreateOpaqueMaterial: + { + std::wstring albedo {}, normal {}, tangent {}, emissive {}, rough {}, metal {}, height {}, sstrans {}, ssthick {}, ssscatter {}; + remixapi_MaterialInfo info = {}; + { + info.sType = pullSType(); + info.hash = pullUInt64(); + albedo = pullPath(); info.albedoTexture = albedo.c_str(); + normal = pullPath(); info.normalTexture = normal.c_str(); + tangent = pullPath(); info.tangentTexture = tangent.c_str(); + emissive = pullPath(); info.emissiveTexture = emissive.c_str(); + info.emissiveIntensity = pullFloat(); + info.emissiveColorConstant = { pullFloat(), pullFloat(), pullFloat() }; + info.spriteSheetRow = (uint8_t) DeviceBridge::get_data(); + info.spriteSheetCol = (uint8_t) DeviceBridge::get_data(); + info.spriteSheetFps = (uint8_t) DeviceBridge::get_data(); + info.filterMode = (uint8_t) DeviceBridge::get_data(); + info.wrapModeU = (uint8_t) DeviceBridge::get_data(); + info.wrapModeV = (uint8_t) DeviceBridge::get_data(); + } + + remixapi_MaterialInfoOpaqueEXT ext = {}; + { + ext.sType = pullSType(); + rough = pullPath(); ext.roughnessTexture = rough.c_str(); + metal = pullPath(); ext.metallicTexture = metal.c_str(); + ext.anisotropy = pullFloat(); + ext.albedoConstant = { pullFloat(), pullFloat(), pullFloat() }; + ext.opacityConstant = pullFloat(); + ext.roughnessConstant = pullFloat(); + ext.metallicConstant = pullFloat(); + ext.thinFilmThickness_hasvalue = pullUInt32(); + ext.thinFilmThickness_value = pullFloat(); + ext.alphaIsThinFilmThickness = pullUInt32(); + height = pullPath(); ext.heightTexture = height.c_str(); + ext.heightTextureStrength = pullFloat(); + ext.useDrawCallAlphaState = pullUInt32(); // If true, InstanceInfoBlendEXT is used as a source for alpha state + ext.blendType_hasvalue = pullUInt32(); + ext.blendType_value = pullInt(); + ext.invertedBlend = pullUInt32(); + ext.alphaTestType = pullInt(); + ext.alphaReferenceValue = (uint8_t) DeviceBridge::get_data(); + } + + remixapi_MaterialInfoOpaqueSubsurfaceEXT ext_ss = {}; + const remixapi_Bool has_subsurface = pullUInt32(); + if (has_subsurface) { + ext_ss.sType = pullSType(); + sstrans = pullPath(); ext_ss.subsurfaceTransmittanceTexture = sstrans.c_str(); + ssthick = pullPath(); ext_ss.subsurfaceThicknessTexture = ssthick.c_str(); + ssscatter = pullPath(); ext_ss.subsurfaceSingleScatteringAlbedoTexture = ssscatter.c_str(); + ext_ss.subsurfaceTransmittanceColor = { pullFloat(), pullFloat(), pullFloat() }; + ext_ss.subsurfaceMeasurementDistance = pullFloat(); + ext_ss.subsurfaceSingleScatteringAlbedo = { pullFloat(), pullFloat(), pullFloat() }; + ext_ss.subsurfaceVolumetricAnisotropy = pullFloat(); + + // MaterialInfo -> OpaqueSubsurfaceEXT -> OpaqueEXT + ext_ss.pNext = &ext; + info.pNext = &ext_ss; + } else { + info.pNext = &ext; // MaterialInfo -> OpaqueEXT + } + + remixapi_MaterialHandle temp_handle = nullptr; + remix_api::g_remix.CreateMaterial(&info, &temp_handle); + + ServerMessage c(Commands::Bridge_Response, currentUID); + c.send_data(sizeof(uint64_t), &temp_handle); + break; + } + + case Api_CreateTranslucentMaterial: + { + std::wstring albedo {}, normal {}, tangent {}, emissive {}, transmittance {}; + remixapi_MaterialInfo info = {}; + { + info.sType = pullSType(); + info.hash = pullUInt64(); + albedo = pullPath(); info.albedoTexture = albedo.c_str(); + normal = pullPath(); info.normalTexture = normal.c_str(); + tangent = pullPath(); info.tangentTexture = tangent.c_str(); + emissive = pullPath(); info.emissiveTexture = emissive.c_str(); + + info.emissiveIntensity = pullFloat(); + info.emissiveColorConstant = { pullFloat(), pullFloat(), pullFloat() }; + info.spriteSheetRow = (uint8_t) DeviceBridge::get_data(); + info.spriteSheetCol = (uint8_t) DeviceBridge::get_data(); + info.spriteSheetFps = (uint8_t) DeviceBridge::get_data(); + info.filterMode = (uint8_t) DeviceBridge::get_data(); + info.wrapModeU = (uint8_t) DeviceBridge::get_data(); + info.wrapModeV = (uint8_t) DeviceBridge::get_data(); + } + + remixapi_MaterialInfoTranslucentEXT ext = {}; + { + ext.sType = pullSType(); + transmittance = pullPath(); ext.transmittanceTexture = transmittance.c_str(); + ext.refractiveIndex = pullFloat(); + ext.transmittanceColor = { pullFloat(), pullFloat(), pullFloat() }; + ext.transmittanceMeasurementDistance = pullFloat(); + ext.thinWallThickness_hasvalue = pullUInt32(); + ext.thinWallThickness_value = pullFloat(); + ext.useDiffuseLayer = pullUInt32(); + } + + // assign ext + info.pNext = &ext; + + remixapi_MaterialHandle temp_handle = nullptr; + remix_api::g_remix.CreateMaterial(&info, &temp_handle); + + ServerMessage c(Commands::Bridge_Response, currentUID); + c.send_data(sizeof(uint64_t), &temp_handle); + break; + } + + case Api_CreatePortalMaterial: + { + std::wstring albedo {}, normal {}, tangent {}, emissive {}; + remixapi_MaterialInfo info = {}; + { + info.sType = pullSType(); + info.hash = pullUInt64(); + albedo = pullPath(); info.albedoTexture = albedo.c_str(); + normal = pullPath(); info.normalTexture = normal.c_str(); + tangent = pullPath(); info.tangentTexture = tangent.c_str(); + emissive = pullPath(); info.emissiveTexture = emissive.c_str(); + + info.emissiveIntensity = pullFloat(); + info.emissiveColorConstant = { pullFloat(), pullFloat(), pullFloat() }; + info.spriteSheetRow = (uint8_t) DeviceBridge::get_data(); + info.spriteSheetCol = (uint8_t) DeviceBridge::get_data(); + info.spriteSheetFps = (uint8_t) DeviceBridge::get_data(); + info.filterMode = (uint8_t) DeviceBridge::get_data(); + info.wrapModeU = (uint8_t) DeviceBridge::get_data(); + info.wrapModeV = (uint8_t) DeviceBridge::get_data(); + } + + remixapi_MaterialInfoPortalEXT ext = {}; + { + ext.sType = pullSType(); + ext.rayPortalIndex = (uint8_t) DeviceBridge::get_data(); + ext.rotationSpeed = pullFloat(); + } + + // assign ext + info.pNext = &ext; + + remixapi_MaterialHandle temp_handle = nullptr; + remix_api::g_remix.CreateMaterial(&info, &temp_handle); + + ServerMessage c(Commands::Bridge_Response, currentUID); + c.send_data(sizeof(uint64_t), &temp_handle); + break; + } + + case Api_DestroyMaterial: + { + uint64_t material_handle = pullUInt64(); + + if (material_handle) { + remix_api::g_remix.DestroyMaterial((remixapi_MaterialHandle) material_handle); + } else { + Logger::debug("[RemixApi] DestroyMaterial(): Invalid material handle"); + } + break; + } + + case Api_CreateTriangleMesh: + { + remixapi_MeshInfo info = {}; + { + info.sType = pullSType(); + info.hash = pullUInt64(); + info.surfaces_count = pullUInt32(); // surface count before surfaces + } + + std::vector surfs; + surfs.reserve(8); + + std::vector> verts; + std::vector> indices; + + for (uint32_t s = 0u; s < info.surfaces_count; s++) { + // pull all vertices + verts.emplace_back(); // add new vector entry for current surface + + uint64_t vertex_count = pullUInt64(); + for (uint64_t v = 0u; v < vertex_count; v++) { + verts.back().emplace_back(remixapi_HardcodedVertex + { + { pullFloat(), pullFloat(), pullFloat() }, // position + { pullFloat(), pullFloat(), pullFloat() }, // normal + { pullFloat(), pullFloat() }, // texcoord + pullUInt32() // color + }); + } + + // pull all indices + indices.emplace_back(); // add new vector entry for current surface + + uint64_t index_count = pullUInt64(); + for (uint64_t i = 0u; i < index_count; i++) { + indices.back().emplace_back(pullUInt32()); + } + + uint32_t skinning_hasvalue = pullUInt32(); + uint64_t material_handle = pullUInt64(); + + // build the surface struct + surfs.emplace_back(remixapi_MeshInfoSurfaceTriangles { + verts.back().data(), + vertex_count, + indices.back().data(), + index_count, + skinning_hasvalue, + remixapi_MeshInfoSkinning {}, + (remixapi_MaterialHandle) material_handle + }); + } + + // remixapi_MeshInfo + info.surfaces_values = surfs.data(); + + remixapi_MeshHandle temp_handle = nullptr; + remix_api::g_remix.CreateMesh(&info, &temp_handle); + + ServerMessage c(Commands::Bridge_Response, currentUID); + c.send_data(sizeof(uint64_t), &temp_handle); + break; + } + + case Api_DestroyMesh: + { + uint64_t mesh_handle = pullUInt64(); + + if (mesh_handle) { + remix_api::g_remix.DestroyMesh((remixapi_MeshHandle) mesh_handle); + } else { + Logger::debug("[RemixApi] DestroyMesh(): Invalid mesh handle"); + } + break; + } + + case Api_DrawMeshInstance: + { + uint64_t mesh_handle = pullUInt64(); + + remixapi_InstanceInfo inst = {}; + { + inst.sType = REMIXAPI_STRUCT_TYPE_INSTANCE_INFO; + inst.categoryFlags = 0; + inst.mesh = (remixapi_MeshHandle) mesh_handle; + inst.transform = {{ + { pullFloat(), pullFloat(), pullFloat(), pullFloat() }, + { pullFloat(), pullFloat(), pullFloat(), pullFloat() }, + { pullFloat(), pullFloat(), pullFloat(), pullFloat() } + }}; + inst.doubleSided = pullUInt32(); + } + + if (mesh_handle) { + remix_api::g_remix.DrawInstance(&inst); + } else { + Logger::debug("[RemixApi] DrawInstance(): Invalid mesh handle"); + } + break; + } + + case Api_CreateSphereLight: + { + remixapi_LightInfo l = {}; + { + l.sType = pullSType(); + l.hash = pullUInt64(); + l.radiance = { pullFloat(), pullFloat(), pullFloat() }; + } + + remixapi_LightInfoSphereEXT ext = {}; + { + ext.sType = pullSType(); + ext.pNext = nullptr; + ext.position = { pullFloat(), pullFloat(), pullFloat() }; + ext.radius = pullFloat(); + ext.shaping_hasvalue = pullUInt32(); + + if (ext.shaping_hasvalue) { + ext.shaping_value.direction = { pullFloat(), pullFloat(), pullFloat() }; + ext.shaping_value.coneAngleDegrees = pullFloat(); + ext.shaping_value.coneSoftness = pullFloat(); + ext.shaping_value.focusExponent = pullFloat(); + } + } + + // remixapi_LightInfo + l.pNext = &ext; + + remixapi_LightHandle temp_handle = nullptr; + remix_api::g_remix.CreateLight(&l, &temp_handle); + + ServerMessage c(Commands::Bridge_Response, currentUID); + c.send_data(sizeof(uint64_t), &temp_handle); + break; + } + + case Api_CreateRectLight: + { + remixapi_LightInfo l = {}; + { + l.sType = pullSType(); + l.hash = pullUInt64(); + l.radiance = { pullFloat(), pullFloat(), pullFloat() }; + } + + remixapi_LightInfoRectEXT ext = {}; + { + ext.sType = pullSType(); + ext.pNext = nullptr; + ext.position = { pullFloat(), pullFloat(), pullFloat() }; + ext.xAxis = { pullFloat(), pullFloat(), pullFloat() }; + ext.xSize = pullFloat(); + ext.yAxis = { pullFloat(), pullFloat(), pullFloat() }; + ext.ySize = pullFloat(); + ext.direction = { pullFloat(), pullFloat(), pullFloat() }; + ext.shaping_hasvalue = pullUInt32(); + + if (ext.shaping_hasvalue) { + ext.shaping_value.direction = { pullFloat(), pullFloat(), pullFloat() }; + ext.shaping_value.coneAngleDegrees = pullFloat(); + ext.shaping_value.coneSoftness = pullFloat(); + ext.shaping_value.focusExponent = pullFloat(); + } + } + + // remixapi_LightInfo + l.pNext = &ext; + + remixapi_LightHandle temp_handle = nullptr; + remix_api::g_remix.CreateLight(&l, &temp_handle); + + ServerMessage c(Commands::Bridge_Response, currentUID); + c.send_data(sizeof(uint64_t), &temp_handle); + break; + } + + case Api_CreateDiskLight: + { + remixapi_LightInfo l = {}; + { + l.sType = pullSType(); + l.hash = pullUInt64(); + l.radiance = { pullFloat(), pullFloat(), pullFloat() }; + } + + remixapi_LightInfoDiskEXT ext = {}; + { + ext.sType = pullSType(); + ext.pNext = nullptr; + ext.position = { pullFloat(), pullFloat(), pullFloat() }; + ext.xAxis = { pullFloat(), pullFloat(), pullFloat() }; + ext.xRadius = pullFloat(); + ext.yAxis = { pullFloat(), pullFloat(), pullFloat() }; + ext.yRadius = pullFloat(); + ext.direction = { pullFloat(), pullFloat(), pullFloat() }; + ext.shaping_hasvalue = pullUInt32(); + + if (ext.shaping_hasvalue) { + ext.shaping_value.direction = { pullFloat(), pullFloat(), pullFloat() }; + ext.shaping_value.coneAngleDegrees = pullFloat(); + ext.shaping_value.coneSoftness = pullFloat(); + ext.shaping_value.focusExponent = pullFloat(); + } + } + + // remixapi_LightInfo + l.pNext = &ext; + + remixapi_LightHandle temp_handle = nullptr; + remix_api::g_remix.CreateLight(&l, &temp_handle); + + ServerMessage c(Commands::Bridge_Response, currentUID); + c.send_data(sizeof(uint64_t), &temp_handle); + break; + } + + case Api_CreateCylinderLight: + { + remixapi_LightInfo l = {}; + { + l.sType = pullSType(); + l.hash = pullUInt64(); + l.radiance = { pullFloat(), pullFloat(), pullFloat() }; + } + + remixapi_LightInfoCylinderEXT ext = {}; + { + ext.sType = pullSType(); + ext.pNext = nullptr; + ext.position = { pullFloat(), pullFloat(), pullFloat() }; + ext.radius = pullFloat(); + ext.axis = { pullFloat(), pullFloat(), pullFloat() }; + ext.axisLength = pullFloat(); + } + + // remixapi_LightInfo + l.pNext = &ext; + + remixapi_LightHandle temp_handle = nullptr; + remix_api::g_remix.CreateLight(&l, &temp_handle); + + ServerMessage c(Commands::Bridge_Response, currentUID); + c.send_data(sizeof(uint64_t), &temp_handle); + break; + } + + case Api_CreateDistantLight: + { + remixapi_LightInfo l = {}; + { + l.sType = pullSType(); + l.hash = pullUInt64(); + l.radiance = { pullFloat(), pullFloat(), pullFloat() }; + } + + remixapi_LightInfoDistantEXT ext = {}; + { + ext.sType = pullSType(); + ext.pNext = nullptr; + ext.direction = { pullFloat(), pullFloat(), pullFloat() }; + ext.angularDiameterDegrees = pullFloat(); + } + + // remixapi_LightInfo + l.pNext = &ext; + + remixapi_LightHandle temp_handle = nullptr; + remix_api::g_remix.CreateLight(&l, &temp_handle); + + ServerMessage c(Commands::Bridge_Response, currentUID); + c.send_data(sizeof(uint64_t), &temp_handle); + break; + } + + case Api_DestroyLight: + { + uint64_t light_handle = pullUInt64(); + + if (light_handle) { + remix_api::g_remix.DestroyLight((remixapi_LightHandle) light_handle); + } else { + Logger::debug("[RemixApi] DestroyLight(): invalid light handle"); + } + break; + } + + case Api_DrawLightInstance: + { + uint64_t light_handle = pullUInt64(); + + if (light_handle) { + remix_api::g_remix.DrawLightInstance((remixapi_LightHandle) light_handle); + } else { + Logger::debug("[RemixApi] DrawLightInstance(): invalid light handle"); + } + break; + } + + case Api_SetConfigVariable: + { + // the returned size of the string is correct but the const char* + // might not be null terminated correctly so its possible that it + // contains junk data at the end due to the 4 byte sized rpc chuncks? + + void* var_ptr = nullptr; + const uint32_t var_size = DeviceBridge::getReaderChannel().data->pull(&var_ptr); + std::string var_str((const char*) var_ptr, var_size); + + void* value_ptr = nullptr; + const uint32_t value_size = DeviceBridge::getReaderChannel().data->pull(&value_ptr); + std::string value_str((const char*) value_ptr, value_size); + + remix_api::g_remix.SetConfigVariable(var_str.c_str(), value_str.c_str()); + break; + } + + case Api_RegisterDevice: + { + if (remix_api::g_remix_initialized) { + if (const auto dev = remix_api::getDevice(); dev) { + remixapi_ErrorCode r = remix_api::g_remix.dxvk_RegisterD3D9Device(dev); + Logger::info("[RemixApi] dxvk_RegisterD3D9Device(): " + (!r ? "success" : "error: " + std::to_string(r))); + } else { + Logger::warn("[RemixApi] Failed to get d3d9 device!"); + } + } + break; + } default: break; } @@ -2784,6 +3327,16 @@ bool InitializeD3D() { } else { Logger::info("D3D9 interface object creation succeeded!"); } + // Initialize remixApi + if (ClientOptions::getExposeRemixApi()) { + remixapi_ErrorCode status = remixapi_lib_loadRemixDllAndInitialize(L"d3d9.dll", &remix_api::g_remix, &remix_api::g_remix_dll); + if (status != REMIXAPI_ERROR_CODE_SUCCESS) { + Logger::err(format_string("[RemixApi] RemixApi initialization failed: %d\n", status)); + } else { + remix_api::g_remix_initialized = true; + Logger::info("[RemixApi] Initialized RemixApi."); + } + } } else { Logger::err(format_string("d3d9.dll loading failed: %ld\n", GetLastError())); return false; diff --git a/src/server/meson.build b/src/server/meson.build index 5c2ad2a..015a3ab 100644 --- a/src/server/meson.build +++ b/src/server/meson.build @@ -44,11 +44,13 @@ server_resource = windows.compile_resources('NvRemixBridge.rc', depend_files : ['NvRemixBridge.ico'] ) +remixapi_include_path = include_directories('../api') + server_exe = executable('NvRemixBridge', server_src, server_header, server_version, server_resource, sources : [ bridge_version ], build_by_default : (cpu_family == 'x86_64') ? true : false, dependencies : [ thread_dep, util_dep, lib_version, tracy_dep ], -include_directories : [ bridge_include_path, util_include_path ], +include_directories : [ bridge_include_path, util_include_path, remixapi_include_path ], win_subsystem : 'windows') diff --git a/src/server/module_processing.cpp b/src/server/module_processing.cpp index f246ffa..5a5e583 100644 --- a/src/server/module_processing.cpp +++ b/src/server/module_processing.cpp @@ -26,6 +26,25 @@ extern std::unordered_map gpD3DSwapChains; extern std::mutex gLock; +namespace remix_api { + remixapi_Interface g_remix = {}; + bool g_remix_initialized = false; + HMODULE g_remix_dll = nullptr; + IDirect3DDevice9Ex* g_device = nullptr; + std::mutex g_device_mutex; + + IDirect3DDevice9Ex* getDevice() { + std::scoped_lock device_lock(g_device_mutex); + if (g_device) { + Logger::info("[RemixApi] getDevice(): success"); + return g_device; + } + + Logger::info("[RemixApi] getDevice(): failed"); + return nullptr; + } +} + #define PULL(type, name) const auto& name = (type)ModuleBridge::get_data() #define PULL_I(name) PULL(INT, name) #define PULL_U(name) PULL(UINT, name) @@ -366,6 +385,7 @@ void processModuleCommandQueue(std::atomic* const pbSignalEnd) { } else { Logger::info("Server side D3D9 DeviceEx created successfully!"); gpD3DDevices[pHandle] = pD3DDevice; + remix_api::g_device = pD3DDevice; } // Send response back to the client @@ -397,6 +417,7 @@ void processModuleCommandQueue(std::atomic* const pbSignalEnd) { } else { Logger::info("Server side D3D9 Device created successfully!"); gpD3DDevices[pHandle] = (IDirect3DDevice9Ex*) pD3DDevice; + remix_api::g_device = (IDirect3DDevice9Ex*) pD3DDevice; } // Send response back to the client diff --git a/src/server/module_processing.h b/src/server/module_processing.h index ce9ff09..065e757 100644 --- a/src/server/module_processing.h +++ b/src/server/module_processing.h @@ -2,4 +2,16 @@ #include -void processModuleCommandQueue(std::atomic* const bSignalEnd); \ No newline at end of file +#include "remix_api/remix_c.h" +#include + +void processModuleCommandQueue(std::atomic* const bSignalEnd); + +namespace remix_api { + extern remixapi_Interface g_remix; + extern bool g_remix_initialized; + extern HMODULE g_remix_dll; + extern IDirect3DDevice9Ex* g_device; + extern std::mutex g_device_mutex; + extern IDirect3DDevice9Ex* getDevice(); +} diff --git a/src/util/util_commands.h b/src/util/util_commands.h index 2ae993c..091ea63 100644 --- a/src/util/util_commands.h +++ b/src/util/util_commands.h @@ -28,7 +28,7 @@ namespace Commands { // The complete set of D3D9 interfaces - enum D3D9Command: uint16_t { + enum D3D9Command : uint16_t { Bridge_Terminate = std::numeric_limits::max(), Bridge_Invalid = 0, Bridge_Syn, @@ -38,6 +38,24 @@ namespace Commands { Bridge_Response, Bridge_DebugMessage, + Api_DebugPrint, + Api_CreateOpaqueMaterial, + Api_CreateTranslucentMaterial, + Api_CreatePortalMaterial, + Api_DestroyMaterial, + Api_CreateTriangleMesh, + Api_DestroyMesh, + Api_DrawMeshInstance, + Api_CreateSphereLight, + Api_CreateRectLight, + Api_CreateDiskLight, + Api_CreateCylinderLight, + Api_CreateDistantLight, + Api_DestroyLight, + Api_DrawLightInstance, + Api_SetConfigVariable, + Api_RegisterDevice, + Bridge_SharedHeap_AddSeg, Bridge_SharedHeap_Alloc, Bridge_SharedHeap_Dealloc, @@ -464,6 +482,24 @@ namespace Commands { case Bridge_Response: return "Response"; case Bridge_DebugMessage: return "DebugMessage"; + case Api_DebugPrint: return "ApiDebugPrint"; + case Api_CreateOpaqueMaterial: return "ApiCreateOpaqueMaterial"; + case Api_CreateTranslucentMaterial: return "ApiCreateTranslucentMaterial"; + case Api_CreatePortalMaterial: return "ApiCreatePortalMaterial"; + case Api_DestroyMaterial: return "ApiDestroyMaterial"; + case Api_CreateTriangleMesh: return "ApiCreateTriangleMesh"; + case Api_DestroyMesh: return "ApiDestroyMesh"; + case Api_DrawMeshInstance: return "ApiDrawMeshInstance"; + case Api_CreateSphereLight: return "ApiCreateSphereLight"; + case Api_CreateRectLight: return "ApiCreateRectLight"; + case Api_CreateDiskLight: return "ApiCreateDiskLight"; + case Api_CreateCylinderLight: return "ApiCreateCylinderLight"; + case Api_CreateDistantLight: return "ApiCreateDistantLight"; + case Api_DestroyLight: return "ApiDestroyLight"; + case Api_DrawLightInstance: return "DrawLightInstance"; + case Api_SetConfigVariable: return "ApiSetConfigVariable"; + case Api_RegisterDevice: return "ApiRegisterDevice"; + case Bridge_SharedHeap_AddSeg: return "SharedHeap_AddSeg"; case Bridge_SharedHeap_Alloc: return "SharedHeap_Alloc"; case Bridge_SharedHeap_Dealloc: return "SharedHeap_Dealloc"; From c7a4067aa88a3bc21296a91b83bf90ace55f030a Mon Sep 17 00:00:00 2001 From: Nicholas Freybler Date: Wed, 18 Sep 2024 16:39:32 -0400 Subject: [PATCH 2/2] - [REMIX-3433] - https://github.com/NVIDIAGameWorks/bridge-remix/pull/12 - Includes Remix API headers under new ext/remix/ top-level dir - remix_c.h and remix.h - Added an #ifndef to be able to turn off non-x64 compiler error at top of remix_c.h - Add new root dir public/include/ for bridge-specific Remix API header - public/include/remixapi/bridge_remix_api.h - Adds new util Serializable helper class in util_serializable.h - Defines a uniform interface of functions to de-/serialize generic classes - Adds new RemixApi util helpers in util_remixapi.h/.cpp - Defines Serializables for remixapi types, and adds extra helpers to handle their quirks - Adds remix_api.h/.cpp to both Client and Server - Helpers for handling the x86 -> x64 conversion --- .gitignore | 1 + bridge.conf | 11 +- ext/remix/remix.h | 994 +++++++++++++++++++++ {src/api/remix_api => ext/remix}/remix_c.h | 7 +- meson.build | 43 +- public/include/remixapi/bridge_remix_api.h | 102 +++ src/api/remix_api/bridge_c.h | 404 --------- src/client/client_options.h | 4 - src/client/d3d9_device.cpp | 58 +- src/client/d3d9_lss.cpp | 3 +- src/client/d3d9_lss.def | 3 + src/client/d3d9_module.cpp | 2 +- src/client/meson.build | 4 +- src/client/remix_api.cpp | 815 ++++++++--------- src/client/remix_api.h | 14 +- src/meson.build | 15 +- src/server/main.cpp | 789 +++++++--------- src/server/meson.build | 10 +- src/server/module_processing.cpp | 25 +- src/server/module_processing.h | 12 - src/server/remix_api.cpp | 44 + src/server/remix_api.h | 54 ++ src/util/config/global_options.h | 8 + src/util/log/log.cpp | 7 + src/util/log/log.h | 1 + src/util/log/log_strings.h | 51 ++ src/util/meson.build | 6 +- src/util/util_bridgecommand.cpp | 7 +- src/util/util_circularbuffer.h | 6 +- src/util/util_circularqueue.h | 8 +- src/util/util_commands.h | 56 +- src/util/util_remixapi.cpp | 773 ++++++++++++++++ src/util/util_remixapi.h | 151 ++++ src/util/util_serializable.h | 271 ++++++ 34 files changed, 3332 insertions(+), 1427 deletions(-) create mode 100644 ext/remix/remix.h rename {src/api/remix_api => ext/remix}/remix_c.h (99%) create mode 100644 public/include/remixapi/bridge_remix_api.h delete mode 100644 src/api/remix_api/bridge_c.h create mode 100644 src/server/remix_api.cpp create mode 100644 src/server/remix_api.h create mode 100644 src/util/log/log_strings.h create mode 100644 src/util/util_remixapi.cpp create mode 100644 src/util/util_remixapi.h create mode 100644 src/util/util_serializable.h diff --git a/.gitignore b/.gitignore index db1f74f..ea67446 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ _output/ _output _comp* +_Comp* _vs/** .history diff --git a/bridge.conf b/bridge.conf index 3ff01a6..acf6f02 100644 --- a/bridge.conf +++ b/bridge.conf @@ -86,8 +86,8 @@ # 2 (Remix UI Active) - Forward DI input only when Remix UI is active. # 3 (Always) - DI input is always forwarded. -# client.DirectInput.forward.mousePolicy = False -# client.DirectInput.forward.keyboardPolicy = False +# client.DirectInput.forward.mousePolicy = 2 +# client.DirectInput.forward.keyboardPolicy = 2 # Forces windowed mode even for games that try to launch in fullscreen, @@ -109,8 +109,11 @@ # client.enableDpiAwareness = True -# Exposes the RemixApi to the client allowing 32-bit games -# to use parts of the RemixApi. +# Exposes Remix API through the bridge, allowing d3d9-hooked applications +# to call API functions directly, as opposed to going through the d3d9 +# API. +# !!! EXPERIMENTAL !!! Defaults off until extensive testing results in +# full validation. # # Supported values: True, False diff --git a/ext/remix/remix.h b/ext/remix/remix.h new file mode 100644 index 0000000..af6942e --- /dev/null +++ b/ext/remix/remix.h @@ -0,0 +1,994 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ + +#pragma once + +#include "remix_c.h" + +#include +#include +#include +#include +#include + +#ifndef REMIXAPI_ASSERT +#include +#define REMIXAPI_ASSERT(mustBeTrue) assert(mustBeTrue) +#endif + +namespace remix { + namespace detail { + template< typename T > + struct Result { + Result(T&& value) + : m_status { REMIXAPI_ERROR_CODE_SUCCESS } + , m_value { std::forward< T >(value) } { + } + + Result(remixapi_ErrorCode error) + : m_status { error } + , m_value {} { + REMIXAPI_ASSERT(error != REMIXAPI_ERROR_CODE_SUCCESS); + } + + Result(const Result&) = delete; + Result(Result&&) = delete; + Result& operator=(const Result&) = delete; + Result& operator=(Result&&) = delete; + + operator bool() const { + return m_status == REMIXAPI_ERROR_CODE_SUCCESS; + } + + T& value() { + REMIXAPI_ASSERT(bool { *this }); + return m_value; + } + + const T& value() const { + REMIXAPI_ASSERT(bool { *this }); + return m_value; + } + + remixapi_ErrorCode status() const { + return m_status; + } + + const T& operator*() const { + return value(); + } + T& operator*() { + return value(); + } + const T* operator->() const { + return &value(); + } + T* operator->() { + return &value(); + } + + private: + const remixapi_ErrorCode m_status; + T m_value; + }; + + template<> + struct Result< void > { + Result(remixapi_ErrorCode error) : m_status { error } { } + + Result(const Result&) = delete; + Result(Result&&) = delete; + Result& operator=(const Result&) = delete; + Result& operator=(Result&&) = delete; + + operator bool() const { + return m_status == REMIXAPI_ERROR_CODE_SUCCESS; + } + + remixapi_ErrorCode status() const { + return m_status; + } + + private: + const remixapi_ErrorCode m_status; + }; + + template< typename T > + struct Span + { + const T* values{ nullptr }; + uint32_t count{ 0 }; + }; + + template< typename T > + void assign_if(remixapi_Bool& hasvalue, T& value, const std::optional< T >& src) { + if (src) { + hasvalue = true; + value = src.value(); + } else { + hasvalue = false; + } + } + } + + template< typename T > + using Result = detail::Result< T >; + + template< typename T > + using Span = detail::Span< T >; + + using StructType = remixapi_StructType; + using Rect2D = remixapi_Rect2D; + using Float2D = remixapi_Float2D; + using Float3D = remixapi_Float3D; + using Float4D = remixapi_Float4D; + using Transform = remixapi_Transform; + + + + struct MaterialInfo; + struct MeshInfo; + struct CameraInfo; + struct InstanceInfo; + struct LightInfo; + namespace detail { + struct dxvk_ExternalSwapchain; + struct dxvk_VkImage; + } + + struct Interface { + HMODULE m_RemixDLL { nullptr }; + remixapi_Interface m_CInterface {}; + + // Functions + Result< void > Startup(const remixapi_StartupInfo& info); + Result< void > Shutdown(); + Result< void > Present(const remixapi_PresentInfo* info = nullptr); + Result< remixapi_MaterialHandle > CreateMaterial(const remixapi_MaterialInfo& info); + Result< void > DestroyMaterial(remixapi_MaterialHandle handle); + Result< remixapi_MeshHandle > CreateMesh(const remixapi_MeshInfo& info); + Result< void > DestroyMesh(remixapi_MeshHandle handle); + Result< void > SetupCamera(const remixapi_CameraInfo& info); + Result< void > DrawInstance(const remixapi_InstanceInfo& info); + Result< remixapi_LightHandle > CreateLight(const remixapi_LightInfo& info); + Result< void > DestroyLight(remixapi_LightHandle handle); + Result< void > DrawLightInstance(remixapi_LightHandle handle); + Result< void > SetConfigVariable(const char* key, const char* value); + + // DXVK interoperability + Result< IDirect3D9Ex* > dxvk_CreateD3D9(bool editorModeEnabled = false); + Result< void > dxvk_RegisterD3D9Device(IDirect3DDevice9Ex* d3d9Device); + Result< detail::dxvk_ExternalSwapchain > dxvk_GetExternalSwapchain(); + Result< detail::dxvk_VkImage > dxvk_GetVkImage(IDirect3DSurface9* source); + Result< void > dxvk_CopyRenderingOutput(IDirect3DSurface9* destination, + remixapi_dxvk_CopyRenderingOutputType type); + Result< void > dxvk_SetDefaultOutput(remixapi_dxvk_CopyRenderingOutputType type, + const remixapi_Float4D& color); + // Object picking utils + template< typename CallbackLambda > // void( remix::Span objectPickingValues ) + Result< void > pick_RequestObjectPicking(const Rect2D& region, CallbackLambda &&callback); + Result< void > pick_HighlightObjects(const uint32_t* objectPickingValues_values, + uint32_t objectPickingValues_count, + uint8_t colorR, uint8_t colorG, uint8_t colorB); + }; + + namespace lib { + // Helper function to load a .dll of Remix, and initialize it. + // pRemixD3D9DllPath is a path to .dll file, e.g. "C:\dxvk-remix-nv\public\bin\d3d9.dll" + [[nodiscard]] inline Result< Interface > loadRemixDllAndInitialize(const std::filesystem::path& remixD3D9DllPath) { + + remixapi_Interface interfaceInC = {}; + HMODULE remixDll = nullptr; + + remixapi_ErrorCode status = + remixapi_lib_loadRemixDllAndInitialize(remixD3D9DllPath.c_str(), + &interfaceInC, + &remixDll); + if (status != REMIXAPI_ERROR_CODE_SUCCESS) { + REMIXAPI_ASSERT(remixDll == nullptr); + return status; + } + + static_assert(sizeof(remixapi_Interface) == 168, + "Change version, update C++ wrapper when adding new functions"); + + remix::Interface interfaceInCpp = {}; + { + interfaceInCpp.m_RemixDLL = remixDll; + interfaceInCpp.m_CInterface = interfaceInC; + } + static_assert(sizeof(remix::Interface) == + sizeof(interfaceInCpp.m_RemixDLL) + sizeof(interfaceInCpp.m_CInterface), + "Not all members of \'interfaceInCpp\' are set here"); + + return interfaceInCpp; + } + + inline Result shutdownAndUnloadRemixDll(Interface& interfaceInCpp) { + return remixapi_lib_shutdownAndUnloadRemixDll(&interfaceInCpp.m_CInterface, interfaceInCpp.m_RemixDLL); + } + } + + + + inline Result< void > Interface::Startup(const remixapi_StartupInfo& info) { + if (!m_CInterface.Startup) { + return REMIXAPI_ERROR_CODE_NOT_INITIALIZED; + } + return m_CInterface.Startup(&info); + } + + inline Result< void > Interface::Shutdown() { + if (!m_CInterface.Shutdown) { + return REMIXAPI_ERROR_CODE_NOT_INITIALIZED; + } + return m_CInterface.Shutdown(); + } + + inline Result< void > Interface::SetConfigVariable(const char* key, const char* value) { + if (!m_CInterface.SetConfigVariable) { + return REMIXAPI_ERROR_CODE_NOT_INITIALIZED; + } + return m_CInterface.SetConfigVariable(key, value); + } + + inline Result< void > Interface::Present(const remixapi_PresentInfo* info) { + if (!m_CInterface.Present) { + return REMIXAPI_ERROR_CODE_NOT_INITIALIZED; + } + return m_CInterface.Present(info); + } + + + + struct MaterialInfoOpaqueEXT : remixapi_MaterialInfoOpaqueEXT { + MaterialInfoOpaqueEXT() { + sType = REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_OPAQUE_EXT; + pNext = nullptr; + roughnessTexture = {}; + metallicTexture = {}; + anisotropy = 0.0f; + albedoConstant = { 0.2f, 0.2f, 0.2f }; + opacityConstant = 1.0f; + roughnessConstant = 0.5f; + metallicConstant = 0.0f; + thinFilmThickness_hasvalue = false; + thinFilmThickness_value = 200.f; + alphaIsThinFilmThickness = false; + heightTexture = {}; + heightTextureStrength = 0.0f; + useDrawCallAlphaState = true; + blendType_hasvalue = false; + blendType_value = 0; + invertedBlend = false; + alphaTestType = 7; + alphaReferenceValue = 0; + static_assert(sizeof remixapi_MaterialInfoOpaqueEXT == 112); + } + + MaterialInfoOpaqueEXT(const MaterialInfoOpaqueEXT& other) + : remixapi_MaterialInfoOpaqueEXT(other) + , cpp_roughnessTexture(other.cpp_roughnessTexture) + , cpp_metallicTexture(other.cpp_metallicTexture) + , cpp_heightTexture(other.cpp_heightTexture) { + cpp_fixPointers(); + } + MaterialInfoOpaqueEXT(MaterialInfoOpaqueEXT&& other) noexcept + : remixapi_MaterialInfoOpaqueEXT(other) + , cpp_roughnessTexture(std::move(other.cpp_roughnessTexture)) + , cpp_metallicTexture(std::move(other.cpp_metallicTexture)) + , cpp_heightTexture(std::move(other.cpp_heightTexture)) { + cpp_fixPointers(); + } + MaterialInfoOpaqueEXT& operator=(const MaterialInfoOpaqueEXT& other) { + if (this == &other) { + return *this; + } + remixapi_MaterialInfoOpaqueEXT::operator=(other); + cpp_roughnessTexture = other.cpp_roughnessTexture; + cpp_metallicTexture = other.cpp_metallicTexture; + cpp_heightTexture = other.cpp_heightTexture; + cpp_fixPointers(); + return *this; + } + MaterialInfoOpaqueEXT& operator=(MaterialInfoOpaqueEXT&& other) noexcept { + if (this == &other) { + return *this; + } + remixapi_MaterialInfoOpaqueEXT::operator=(other); + cpp_roughnessTexture = std::move(other.cpp_roughnessTexture); + cpp_metallicTexture = std::move(other.cpp_metallicTexture); + cpp_heightTexture = std::move(other.cpp_heightTexture); + cpp_fixPointers(); + return *this; + } + + void set_roughnessTexture(std::filesystem::path v) { + cpp_roughnessTexture = std::move(v); + roughnessTexture = cpp_roughnessTexture.c_str(); + } + void set_metallicTexture(std::filesystem::path v) { + cpp_metallicTexture = std::move(v); + metallicTexture = cpp_metallicTexture.c_str(); + } + void set_heightTexture(std::filesystem::path v) { + cpp_heightTexture = std::move(v); + heightTexture = cpp_heightTexture.c_str(); + } + void set_thinFilmThickness(const std::optional< float >& v) { + detail::assign_if(thinFilmThickness_hasvalue, thinFilmThickness_value, v); + } + void set_blendType(const std::optional< int >& v) { + detail::assign_if(blendType_hasvalue, blendType_value, v); + } + + private: + void cpp_fixPointers() { + roughnessTexture = cpp_roughnessTexture.c_str(); + metallicTexture = cpp_metallicTexture.c_str(); + heightTexture = cpp_heightTexture.c_str(); + static_assert(sizeof remixapi_MaterialInfoOpaqueEXT == 112, "Recheck pointers"); + } + + std::filesystem::path cpp_roughnessTexture {}; + std::filesystem::path cpp_metallicTexture {}; + std::filesystem::path cpp_heightTexture {}; + }; + + // Can be linked to MaterialInfoOpaqueEXT + struct MaterialInfoOpaqueSubsurfaceEXT : remixapi_MaterialInfoOpaqueSubsurfaceEXT { + MaterialInfoOpaqueSubsurfaceEXT() { + sType = REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_OPAQUE_SUBSURFACE_EXT; + pNext = nullptr; + subsurfaceTransmittanceTexture = {}; + subsurfaceThicknessTexture = {}; + subsurfaceSingleScatteringAlbedoTexture = {}; + subsurfaceTransmittanceColor = { 0.5f, 0.5f, 0.5f }; + subsurfaceMeasurementDistance = 0.0f; + subsurfaceSingleScatteringAlbedo = { 0.5f, 0.5f, 0.5f };; + subsurfaceVolumetricAnisotropy = 0.0f; + static_assert(sizeof remixapi_MaterialInfoOpaqueSubsurfaceEXT == 72); + } + + MaterialInfoOpaqueSubsurfaceEXT(const MaterialInfoOpaqueSubsurfaceEXT& other) + : remixapi_MaterialInfoOpaqueSubsurfaceEXT(other) + , cpp_subsurfaceTransmittanceTexture(other.cpp_subsurfaceTransmittanceTexture) + , cpp_subsurfaceThicknessTexture(other.cpp_subsurfaceThicknessTexture) + , cpp_subsurfaceSingleScatteringAlbedoTexture(other.cpp_subsurfaceSingleScatteringAlbedoTexture) { + cpp_fixPointers(); + } + MaterialInfoOpaqueSubsurfaceEXT(MaterialInfoOpaqueSubsurfaceEXT&& other) noexcept + : remixapi_MaterialInfoOpaqueSubsurfaceEXT(other) + , cpp_subsurfaceTransmittanceTexture(std::move(other.cpp_subsurfaceTransmittanceTexture)) + , cpp_subsurfaceThicknessTexture(std::move(other.cpp_subsurfaceThicknessTexture)) + , cpp_subsurfaceSingleScatteringAlbedoTexture(std::move(other.cpp_subsurfaceSingleScatteringAlbedoTexture)) { + cpp_fixPointers(); + } + MaterialInfoOpaqueSubsurfaceEXT& operator=(const MaterialInfoOpaqueSubsurfaceEXT& other) { + if (this == &other) { + return *this; + } + remixapi_MaterialInfoOpaqueSubsurfaceEXT::operator=(other); + cpp_subsurfaceTransmittanceTexture = other.cpp_subsurfaceTransmittanceTexture; + cpp_subsurfaceThicknessTexture = other.cpp_subsurfaceThicknessTexture; + cpp_subsurfaceSingleScatteringAlbedoTexture = other.cpp_subsurfaceSingleScatteringAlbedoTexture; + cpp_fixPointers(); + return *this; + } + MaterialInfoOpaqueSubsurfaceEXT& operator=(MaterialInfoOpaqueSubsurfaceEXT&& other) noexcept { + if (this == &other) { + return *this; + } + remixapi_MaterialInfoOpaqueSubsurfaceEXT::operator=(other); + cpp_subsurfaceTransmittanceTexture = std::move(other.cpp_subsurfaceTransmittanceTexture); + cpp_subsurfaceThicknessTexture = std::move(other.cpp_subsurfaceThicknessTexture); + cpp_subsurfaceSingleScatteringAlbedoTexture = std::move(other.cpp_subsurfaceSingleScatteringAlbedoTexture); + cpp_fixPointers(); + return *this; + } + + void set_subsurfaceTransmittanceTexture(std::filesystem::path v) { + cpp_subsurfaceTransmittanceTexture = std::move(v); + subsurfaceTransmittanceTexture = cpp_subsurfaceTransmittanceTexture.c_str(); + } + void set_subsurfaceThicknessTexture(std::filesystem::path v) { + cpp_subsurfaceThicknessTexture = std::move(v); + subsurfaceThicknessTexture = cpp_subsurfaceThicknessTexture.c_str(); + } + void set_subsurfaceSingleScatteringAlbedoTexture(std::filesystem::path v) { + cpp_subsurfaceSingleScatteringAlbedoTexture = std::move(v); + subsurfaceSingleScatteringAlbedoTexture = cpp_subsurfaceSingleScatteringAlbedoTexture.c_str(); + } + + private: + void cpp_fixPointers() { + subsurfaceTransmittanceTexture = cpp_subsurfaceTransmittanceTexture.c_str(); + subsurfaceThicknessTexture = cpp_subsurfaceThicknessTexture.c_str(); + subsurfaceSingleScatteringAlbedoTexture = cpp_subsurfaceSingleScatteringAlbedoTexture.c_str(); + static_assert(sizeof remixapi_MaterialInfoOpaqueSubsurfaceEXT == 72, "Recheck pointers"); + } + + std::filesystem::path cpp_subsurfaceTransmittanceTexture {}; + std::filesystem::path cpp_subsurfaceThicknessTexture {}; + std::filesystem::path cpp_subsurfaceSingleScatteringAlbedoTexture {}; + }; + + struct MaterialInfoTranslucentEXT : remixapi_MaterialInfoTranslucentEXT { + MaterialInfoTranslucentEXT() { + sType = REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_TRANSLUCENT_EXT; + pNext = nullptr; + transmittanceTexture = {}; + refractiveIndex = 1.3f; + transmittanceColor = { 0.97f, 0.97f, 0.97f }; + transmittanceMeasurementDistance = 1.0f; + thinWallThickness_hasvalue = false; + thinWallThickness_value = 0.001f; + useDiffuseLayer = false; + static_assert(sizeof remixapi_MaterialInfoTranslucentEXT == 56); + } + + MaterialInfoTranslucentEXT(const MaterialInfoTranslucentEXT& other) + : remixapi_MaterialInfoTranslucentEXT(other) + , cpp_transmittanceTexture(other.cpp_transmittanceTexture) { + cpp_fixPointers(); + } + MaterialInfoTranslucentEXT(MaterialInfoTranslucentEXT&& other) noexcept + : remixapi_MaterialInfoTranslucentEXT(other) + , cpp_transmittanceTexture(std::move(other.cpp_transmittanceTexture)) { + cpp_fixPointers(); + } + MaterialInfoTranslucentEXT& operator=(const MaterialInfoTranslucentEXT& other) { + if (this == &other) { + return *this; + } + remixapi_MaterialInfoTranslucentEXT::operator=(other); + cpp_transmittanceTexture = other.cpp_transmittanceTexture; + cpp_fixPointers(); + return *this; + } + MaterialInfoTranslucentEXT& operator=(MaterialInfoTranslucentEXT&& other) noexcept { + if (this == &other) { + return *this; + } + remixapi_MaterialInfoTranslucentEXT::operator=(other); + cpp_transmittanceTexture = std::move(other.cpp_transmittanceTexture); + cpp_fixPointers(); + return *this; + } + + void set_transmittanceTexture(std::filesystem::path v) { + cpp_transmittanceTexture = std::move(v); + transmittanceTexture = cpp_transmittanceTexture.c_str(); + } + void set_thinWallThickness(const std::optional< float >& v) { + detail::assign_if(thinWallThickness_hasvalue, thinWallThickness_value, v); + } + + private: + void cpp_fixPointers() { + transmittanceTexture = cpp_transmittanceTexture.c_str(); + static_assert(sizeof remixapi_MaterialInfoTranslucentEXT == 56, "Recheck pointers"); + } + + std::filesystem::path cpp_transmittanceTexture {}; + }; + + struct MaterialInfoPortalEXT : remixapi_MaterialInfoPortalEXT { + MaterialInfoPortalEXT() { + sType = REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_PORTAL_EXT; + pNext = nullptr; + rayPortalIndex = 0; + rotationSpeed = 0.0f; + static_assert(sizeof remixapi_MaterialInfoPortalEXT == 24); + } + }; + + struct MaterialInfo : remixapi_MaterialInfo { + MaterialInfo() { + sType = REMIXAPI_STRUCT_TYPE_MATERIAL_INFO; + pNext = nullptr; + hash = 0; + albedoTexture = {}; + normalTexture = {}; + tangentTexture = {}; + emissiveTexture = {}; + emissiveIntensity = 40.0f; + emissiveColorConstant = { 0.0f, 0.0f, 0.0f }; + spriteSheetRow = 1; + spriteSheetCol = 1; + spriteSheetFps = 0; + filterMode = 1; // Linear + wrapModeU = 1; // Repeat + wrapModeV = 1; // Repeat + static_assert(sizeof remixapi_MaterialInfo == 80); + } + + MaterialInfo(const MaterialInfo& other) + : remixapi_MaterialInfo(other) + , cpp_albedoTexture(other.cpp_albedoTexture) + , cpp_normalTexture(other.cpp_normalTexture) + , cpp_tangentTexture(other.cpp_tangentTexture) + , cpp_emissiveTexture(other.cpp_emissiveTexture) { + cpp_fixPointers(); + } + MaterialInfo(MaterialInfo&& other) noexcept + : remixapi_MaterialInfo(other) + , cpp_albedoTexture(std::move(other.cpp_albedoTexture)) + , cpp_normalTexture(std::move(other.cpp_normalTexture)) + , cpp_tangentTexture(std::move(other.cpp_tangentTexture)) + , cpp_emissiveTexture(std::move(other.cpp_emissiveTexture)) { + cpp_fixPointers(); + } + MaterialInfo& operator=(const MaterialInfo& other) { + if (this == &other) { + return *this; + } + remixapi_MaterialInfo::operator=(other); + cpp_albedoTexture = other.cpp_albedoTexture; + cpp_normalTexture = other.cpp_normalTexture; + cpp_tangentTexture = other.cpp_tangentTexture; + cpp_emissiveTexture = other.cpp_emissiveTexture; + cpp_fixPointers(); + return *this; + } + MaterialInfo& operator=(MaterialInfo&& other) noexcept { + if (this == &other) { + return *this; + } + remixapi_MaterialInfo::operator=(other); + cpp_albedoTexture = std::move(other.cpp_albedoTexture); + cpp_normalTexture = std::move(other.cpp_normalTexture); + cpp_tangentTexture = std::move(other.cpp_tangentTexture); + cpp_emissiveTexture = std::move(other.cpp_emissiveTexture); + cpp_fixPointers(); + return *this; + } + + void set_albedoTexture(std::filesystem::path v) { + cpp_albedoTexture = std::move(v); + albedoTexture = cpp_albedoTexture.c_str(); + } + void set_normalTexture(std::filesystem::path v) { + cpp_normalTexture = std::move(v); + normalTexture = cpp_normalTexture.c_str(); + } + void set_tangentTexture(std::filesystem::path v) { + cpp_tangentTexture = std::move(v); + tangentTexture = cpp_tangentTexture.c_str(); + } + void set_emissiveTexture(std::filesystem::path v) { + cpp_emissiveTexture = std::move(v); + emissiveTexture = cpp_emissiveTexture.c_str(); + } + + private: + void cpp_fixPointers() { + albedoTexture = cpp_albedoTexture.c_str(); + normalTexture = cpp_normalTexture.c_str(); + tangentTexture = cpp_tangentTexture.c_str(); + emissiveTexture = cpp_emissiveTexture.c_str(); + static_assert(sizeof remixapi_MaterialInfo == 80, "Recheck pointers"); + } + + std::filesystem::path cpp_albedoTexture {}; + std::filesystem::path cpp_normalTexture {}; + std::filesystem::path cpp_tangentTexture {}; + std::filesystem::path cpp_emissiveTexture {}; + }; + + inline Result< remixapi_MaterialHandle > Interface::CreateMaterial(const remixapi_MaterialInfo& info) { + remixapi_MaterialHandle handle = nullptr; + remixapi_ErrorCode status = m_CInterface.CreateMaterial(&info, &handle); + if (status != REMIXAPI_ERROR_CODE_SUCCESS) { + return status; + } + return handle; + } + + inline Result< void > Interface::DestroyMaterial(remixapi_MaterialHandle handle) { + return m_CInterface.DestroyMaterial(handle); + } + + + + struct MeshInfo : remixapi_MeshInfo { + MeshInfo() { + sType = REMIXAPI_STRUCT_TYPE_MESH_INFO; + pNext = nullptr; + hash = 0; + surfaces_values = {}; + surfaces_count = 0; + static_assert(sizeof remixapi_MeshInfo == 40); + } + }; + + inline Result< remixapi_MeshHandle > Interface::CreateMesh(const remixapi_MeshInfo& info) { + remixapi_MeshHandle handle = nullptr; + remixapi_ErrorCode status = m_CInterface.CreateMesh(&info, &handle); + if (status != REMIXAPI_ERROR_CODE_SUCCESS) { + return status; + } + return handle; + } + + inline Result< void > Interface::DestroyMesh(remixapi_MeshHandle handle) { + return m_CInterface.DestroyMesh(handle); + } + + + + using CameraType = remixapi_CameraType; + + // Ignores view / projection matrices from CameraInfo + // by recalculating them from the given arguments in this struct. + struct CameraInfoParameterizedEXT : remixapi_CameraInfoParameterizedEXT { + CameraInfoParameterizedEXT() { + sType = { REMIXAPI_STRUCT_TYPE_CAMERA_INFO_PARAMETERIZED_EXT }; + pNext = { nullptr }; + position = { 0, 0, 0 }; + forward = { 0, 0, 1 }; + up = { 0, 1, 0 }; + right = { 1, 0, 0 }; + fovYInDegrees = 75.f; + aspect = 16.f / 9.f; + nearPlane = 0.1f; + farPlane = 1000.f; + static_assert(sizeof remixapi_CameraInfoParameterizedEXT == 80); + } + }; + + struct CameraInfo : remixapi_CameraInfo { + CameraInfo() { + sType = { REMIXAPI_STRUCT_TYPE_CAMERA_INFO }; + pNext = { nullptr }; + type = { REMIXAPI_CAMERA_TYPE_WORLD }; + view[0][0] = view[1][1] = view[2][2] = view[3][3] = 1.f; + projection[0][0] = projection[1][1] = projection[2][2] = projection[3][3] = 1.f; + static_assert(sizeof remixapi_CameraInfo == 152); + } + }; + + inline Result< void > Interface::SetupCamera(const remixapi_CameraInfo& info) { + return m_CInterface.SetupCamera(&info); + } + + + + struct InstanceInfoBoneTransformsEXT : remixapi_InstanceInfoBoneTransformsEXT { + InstanceInfoBoneTransformsEXT() { + sType = REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_BONE_TRANSFORMS_EXT; + pNext = nullptr; + boneTransforms_count = 0; + boneTransforms_values = {}; + static_assert(sizeof remixapi_InstanceInfoBoneTransformsEXT == 32); + } + }; + + struct InstanceInfoBlendEXT : remixapi_InstanceInfoBlendEXT { + InstanceInfoBlendEXT() { + sType = REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_BLEND_EXT; + pNext = nullptr; + alphaTestEnabled = false; + alphaTestReferenceValue = 0; + alphaTestCompareOp = 7 /* VK_COMPARE_OP_ALWAYS */; + alphaBlendEnabled = false; + srcColorBlendFactor = 1 /* VK_BLEND_FACTOR_ONE */; + dstColorBlendFactor = 0 /* VK_BLEND_FACTOR_ZERO */; + colorBlendOp = 0 /* VK_BLEND_OP_ADD */; + textureColorArg1Source = 1 /* RtTextureArgSource::Texture */; + textureColorArg2Source = 0 /* RtTextureArgSource::None */; + textureColorOperation = 3 /* DxvkRtTextureOperation::Modulate */; + textureAlphaArg1Source = 1 /* RtTextureArgSource::Texture */; + textureAlphaArg2Source = 0 /* RtTextureArgSource::None */; + textureAlphaOperation = 1 /* DxvkRtTextureOperation::SelectArg1 */; + tFactor = 0XFFFFFFFF; + isTextureFactorBlend = false; + static_assert(sizeof remixapi_InstanceInfoBlendEXT == 80); + } + }; + + struct InstanceInfoObjectPickingEXT : remixapi_InstanceInfoObjectPickingEXT { + InstanceInfoObjectPickingEXT() { + sType = REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_OBJECT_PICKING_EXT; + pNext = nullptr; + objectPickingValue = 0; + } + }; + + using InstanceCategoryBit = remixapi_InstanceCategoryBit; + using InstanceCategoryFlags = remixapi_InstanceCategoryFlags; + + struct InstanceInfo : remixapi_InstanceInfo { + InstanceInfo() { + sType = REMIXAPI_STRUCT_TYPE_INSTANCE_INFO; + pNext = nullptr; + categoryFlags = 0; + mesh = 0; + transform = {}; + doubleSided = false; + static_assert(sizeof remixapi_InstanceInfo == 88); + } + }; + + inline Result< void > Interface::DrawInstance(const remixapi_InstanceInfo& info) { + return m_CInterface.DrawInstance(&info); + } + + + + namespace detail { + inline remixapi_LightInfoLightShaping defaultLightShaping() { + remixapi_LightInfoLightShaping shaping {}; + { + shaping.direction = { 0.0f, 0.0f, 1.0f }; + shaping.coneAngleDegrees = 180.0f; + shaping.coneSoftness = 0.0f; + shaping.focusExponent = 0.0f; + } + return shaping; + }; + } + + using LightInfoLightShaping = remixapi_LightInfoLightShaping; + + struct LightInfoSphereEXT : remixapi_LightInfoSphereEXT { + LightInfoSphereEXT() { + sType = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_SPHERE_EXT; + pNext = nullptr; + position = { 0.0f, 0.0f, 0.0f }; + radius = 0.05f; + shaping_hasvalue = false; + shaping_value = detail::defaultLightShaping(); + static_assert(sizeof remixapi_LightInfoSphereEXT == 64); + } + + void set_shaping(const std::optional< remixapi_LightInfoLightShaping >& v) { + detail::assign_if(shaping_hasvalue, shaping_value, v); + } + }; + + struct LightInfoRectEXT : remixapi_LightInfoRectEXT { + LightInfoRectEXT() { + sType = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_RECT_EXT; + pNext = nullptr; + position = { 0.0f, 0.0f, 0.0f }; + xAxis = { 1.0f, 0.0f, 0.0f }; + xSize = 1.0f; + yAxis = { 0.0f, 1.0f, 0.0f }; + ySize = 1.0f; + direction = { 0.0f, 0.0f, 1.0f }; + shaping_hasvalue = false; + shaping_value = detail::defaultLightShaping(); + static_assert(sizeof remixapi_LightInfoRectEXT == 104); + } + + void set_shaping(const std::optional< remixapi_LightInfoLightShaping >& v) { + detail::assign_if(shaping_hasvalue, shaping_value, v); + } + }; + + struct LightInfoDiskEXT : remixapi_LightInfoDiskEXT { + LightInfoDiskEXT() { + sType = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DISK_EXT; + pNext = nullptr; + position = { 0.0f, 0.0f, 0.0f }; + xAxis = { 1.0f, 0.0f, 0.0f }; + xRadius = 1.0f; + yAxis = { 0.0f, 1.0f, 0.0f }; + yRadius = 1.0f; + direction = { 0.0f, 0.0f, 1.0f }; + shaping_hasvalue = false; + shaping_value = detail::defaultLightShaping(); + static_assert(sizeof remixapi_LightInfoDiskEXT == 104); + } + + void set_shaping(const std::optional< remixapi_LightInfoLightShaping >& v) { + detail::assign_if(shaping_hasvalue, shaping_value, v); + } + }; + + struct LightInfoCylinderEXT : remixapi_LightInfoCylinderEXT { + LightInfoCylinderEXT() { + sType = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_CYLINDER_EXT; + pNext = nullptr; + position = { 0.0f, 0.0f, 0.0f }; + radius = 1.0f; + axis = { 1.0f, 0.0f, 0.0f }; + axisLength = 1.0f; + static_assert(sizeof remixapi_LightInfoCylinderEXT == 48); + } + }; + + struct LightInfoDistantEXT : remixapi_LightInfoDistantEXT { + LightInfoDistantEXT() { + sType = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DISTANT_EXT; + pNext = nullptr; + direction = { 0.0f, -1.0f, 0.0f }; + angularDiameterDegrees = 0.5f; + static_assert(sizeof remixapi_LightInfoDistantEXT == 32); + } + }; + + struct LightInfoDomeEXT : remixapi_LightInfoDomeEXT { + LightInfoDomeEXT() { + sType = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DOME_EXT; + pNext = nullptr; + transform = {}; + colorTexture = {}; + static_assert(sizeof remixapi_LightInfoDomeEXT == 72); + } + + LightInfoDomeEXT(const LightInfoDomeEXT& other) + : remixapi_LightInfoDomeEXT(other) + , cpp_colorTexture(other.cpp_colorTexture) { + cpp_fixPointers(); + } + LightInfoDomeEXT(LightInfoDomeEXT&& other) noexcept + : remixapi_LightInfoDomeEXT(other) + , cpp_colorTexture(std::move(other.cpp_colorTexture)) { + cpp_fixPointers(); + } + LightInfoDomeEXT& operator=(const LightInfoDomeEXT& other) { + if (this == &other) { + return *this; + } + remixapi_LightInfoDomeEXT::operator=(other); + cpp_colorTexture = other.cpp_colorTexture; + cpp_fixPointers(); + return *this; + } + LightInfoDomeEXT& operator=(LightInfoDomeEXT&& other) noexcept { + if (this == &other) { + return *this; + } + remixapi_LightInfoDomeEXT::operator=(other); + cpp_colorTexture = std::move(other.cpp_colorTexture); + cpp_fixPointers(); + return *this; + } + + void set_colorTexture(std::filesystem::path v) { + cpp_colorTexture = std::move(v); + colorTexture = cpp_colorTexture.c_str(); + } + + private: + void cpp_fixPointers() { + colorTexture = cpp_colorTexture.c_str(); + static_assert(sizeof remixapi_LightInfoDomeEXT == 72, "Recheck pointers"); + } + + std::filesystem::path cpp_colorTexture {}; + }; + + struct LightInfo : remixapi_LightInfo { + LightInfo() { + sType = REMIXAPI_STRUCT_TYPE_LIGHT_INFO; + pNext = nullptr; + hash = 0; + radiance = { 1.0f, 1.0f, 1.0f }; + static_assert(sizeof remixapi_LightInfo == 40); + } + }; + + inline Result< remixapi_LightHandle > Interface::CreateLight(const remixapi_LightInfo& info) { + remixapi_LightHandle handle = nullptr; + remixapi_ErrorCode status = m_CInterface.CreateLight(&info, &handle); + if (status != REMIXAPI_ERROR_CODE_SUCCESS) { + return status; + } + return handle; + } + + inline Result< void > Interface::DestroyLight(remixapi_LightHandle handle) { + return m_CInterface.DestroyLight(handle); + } + + inline Result< void > Interface::DrawLightInstance(remixapi_LightHandle handle) { + return m_CInterface.DrawLightInstance(handle); + } + + namespace detail { + struct dxvk_ExternalSwapchain { + uint64_t vkImage; + uint64_t vkSemaphoreRenderingDone; + uint64_t vkSemaphoreResumeSemaphore; + }; + + struct dxvk_VkImage { + uint64_t vkImage; + }; + } + + inline Result< IDirect3D9Ex* > Interface::dxvk_CreateD3D9(bool editorModeEnabled) { + IDirect3D9Ex* d3d9 { nullptr }; + remixapi_ErrorCode status = m_CInterface.dxvk_CreateD3D9(editorModeEnabled, &d3d9); + if (status != REMIXAPI_ERROR_CODE_SUCCESS) { + return status; + } + return d3d9; + } + + inline Result< void > Interface::dxvk_RegisterD3D9Device(IDirect3DDevice9Ex* d3d9Device) { + return m_CInterface.dxvk_RegisterD3D9Device(d3d9Device); + } + + inline Result< detail::dxvk_ExternalSwapchain > Interface::dxvk_GetExternalSwapchain() { + detail::dxvk_ExternalSwapchain externalSwapchain {}; + remixapi_ErrorCode status = m_CInterface.dxvk_GetExternalSwapchain( + &externalSwapchain.vkImage, + &externalSwapchain.vkSemaphoreRenderingDone, + &externalSwapchain.vkSemaphoreResumeSemaphore); + if (status != REMIXAPI_ERROR_CODE_SUCCESS) { + return status; + } + return externalSwapchain; + } + + inline Result< detail::dxvk_VkImage > Interface::dxvk_GetVkImage(IDirect3DSurface9* source) { + detail::dxvk_VkImage externalImage {}; + remixapi_ErrorCode status = m_CInterface.dxvk_GetVkImage(source, &externalImage.vkImage); + if (status != REMIXAPI_ERROR_CODE_SUCCESS) { + return status; + } + return externalImage; + } + + inline Result< void > Interface::dxvk_CopyRenderingOutput( + IDirect3DSurface9* destination, remixapi_dxvk_CopyRenderingOutputType type) { + return m_CInterface.dxvk_CopyRenderingOutput(destination, type); + } + + inline Result< void > Interface::dxvk_SetDefaultOutput( + remixapi_dxvk_CopyRenderingOutputType type, const remixapi_Float4D& color) { + return m_CInterface.dxvk_SetDefaultOutput(type, &color); + } + + template< typename CallbackLambda > + inline Result< void > Interface::pick_RequestObjectPicking(const Rect2D& region, CallbackLambda&& callback) { + using Func = std::function< void(Span) >; + + static auto bootstrapForC = [](const uint32_t* objectPickingValues_values, + uint32_t objectPickingValues_count, + void* callbackUserData) { + // unwrap 'callbackUserData', it is a user's lambda + if (auto* userLambda = static_cast(callbackUserData)) { + auto arg = Span{ objectPickingValues_values, objectPickingValues_count }; + // call user's lambda + (*userLambda)(arg); + delete userLambda; + } + }; + + // pass user's lambda as as 'callbackUserData' + auto* userLambda = new Func { callback }; + return m_CInterface.pick_RequestObjectPicking(®ion, bootstrapForC, userLambda); + } + + inline Result< void > Interface::pick_HighlightObjects(const uint32_t* objectPickingValues_values, + uint32_t objectPickingValues_count, + uint8_t colorR, uint8_t colorG, uint8_t colorB) { + return m_CInterface.pick_HighlightObjects(objectPickingValues_values, + objectPickingValues_count, + colorR, colorG, colorB); + } +} diff --git a/src/api/remix_api/remix_c.h b/ext/remix/remix_c.h similarity index 99% rename from src/api/remix_api/remix_c.h rename to ext/remix/remix_c.h index 071f6f4..65fd8a4 100644 --- a/src/api/remix_api/remix_c.h +++ b/ext/remix/remix_c.h @@ -26,9 +26,11 @@ #include #include +#ifndef REMIX_ALLOW_X86 #if _WIN64 != 1 #error Remix API requires 64-bit for the ray tracing features. #endif +#endif // __stdcall convention @@ -405,6 +407,7 @@ extern "C" { REMIXAPI_INSTANCE_CATEGORY_BIT_THIRD_PERSON_PLAYER_MODEL = 1 << 18, REMIXAPI_INSTANCE_CATEGORY_BIT_THIRD_PERSON_PLAYER_BODY = 1 << 19, REMIXAPI_INSTANCE_CATEGORY_BIT_IGNORE_BAKED_LIGHTING = 1 << 20, + REMIXAPI_INSTANCE_CATEGORY_BIT_IGNORE_ALPHA_CHANNEL = 1 << 21, } remixapi_InstanceCategoryBit; typedef uint32_t remixapi_InstanceCategoryFlags; @@ -781,14 +784,14 @@ extern "C" { return REMIXAPI_ERROR_CODE_GET_PROC_ADDRESS_FAILURE; } - remixapi_InitializeLibraryInfo info = {}; + remixapi_InitializeLibraryInfo info = { 0 }; { info.sType = REMIXAPI_STRUCT_TYPE_INITIALIZE_LIBRARY_INFO; info.version = REMIXAPI_VERSION_MAKE(REMIXAPI_VERSION_MAJOR, REMIXAPI_VERSION_MINOR, REMIXAPI_VERSION_PATCH); } - remixapi_Interface remixInterface = { nullptr }; + remixapi_Interface remixInterface = { 0 }; remixapi_ErrorCode status = pfn_InitializeLibrary(&info, &remixInterface); if (status != REMIXAPI_ERROR_CODE_SUCCESS) { diff --git a/meson.build b/meson.build index 00cc4e2..d5ffd1d 100644 --- a/meson.build +++ b/meson.build @@ -113,6 +113,8 @@ endif bridge_include_path = '' # include_directories('./include') util_include_path = include_directories('./src/util') +public_include_path = include_directories('./public/include/') +ext_include_path = include_directories('./ext/') if (cpu_family == 'x86_64') bridge_library_path = meson.global_source_root() + '/lib' @@ -248,19 +250,20 @@ bridge_version = vcs_tag( input: 'version.h.in', output: 'version.h') -subdir('src') +enable_tests = get_option('enable_tests') # Must be defined before subdir'ing src -enable_tests = get_option('enable_tests') +subdir('src') if enable_tests subdir('test') + subdir_done() endif vs_gen_cmdline = [ vs_project_defines ] # add data from gametargets.conf to bridge_copy_targets game_targets_run = run_command(python_interpreter, - meson.global_source_root() + '\\vsgen\\get_game_target_list.py') + meson.global_source_root() + '\\vsgen\\get_game_target_list.py') game_targets = game_targets_run.stdout().strip() copy_targets = {} @@ -311,20 +314,20 @@ endforeach # Building project files after builds for x86_64 and x86 platforms are created, here builds for x86 platform are created after x86_64 if cpu_family == 'x86' - # generate VS project files for ninja - # note: this writes to /_vs, which is outside the build directory - vsproj = run_command(python_interpreter, - meson.global_source_root() + '\\vsgen\\generate_vs_project_files.py', - vs_gen_cmdline, - check: false) - - if vsproj.stdout().strip() != '' - message(vsproj.stdout().strip()) - endif - if vsproj.stderr().strip() != '' - message(vsproj.stderr().strip()) - endif - if vsproj.returncode() != 0 - error('generate_vs_project_files failed') - endif -endif \ No newline at end of file + # generate VS project files for ninja + # note: this writes to /_vs, which is outside the build directory + vsproj = run_command(python_interpreter, + meson.global_source_root() + '\\vsgen\\generate_vs_project_files.py', + vs_gen_cmdline, + check: false) + + if vsproj.stdout().strip() != '' + message(vsproj.stdout().strip()) + endif + if vsproj.stderr().strip() != '' + message(vsproj.stderr().strip()) + endif + if vsproj.returncode() != 0 + error('generate_vs_project_files failed') + endif +endif diff --git a/public/include/remixapi/bridge_remix_api.h b/public/include/remixapi/bridge_remix_api.h new file mode 100644 index 0000000..e7beee1 --- /dev/null +++ b/public/include/remixapi/bridge_remix_api.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ +#pragma once + +// remix_c.h throws a #error if not targeting x64 arch, because RemixAPI does not +// work on 32-bit arches. However, we are only using the header for its types +// so this should be fine to circumvent +#if _WIN64 != 1 +#define REMIX_ALLOW_X86 +#endif +#include +#if _WIN64 != 1 +#undef REMIX_ALLOW_X86 +#endif +#include + +#if defined(__WINE__) + #define DLLEXPORT __attribute__((visibility("default"))) +#elif defined(_MSC_VER) + #define DLLEXPORT +#else + #define DLLEXPORT __declspec(dllexport) +#endif + +typedef void(__cdecl* PFN_remixapi_BridgeCallback)(void); +typedef remixapi_ErrorCode(DLLEXPORT REMIXAPI_CALL* PFN_remixapi_RegisterCallbacks)( + PFN_remixapi_BridgeCallback beginSceneCallback, + PFN_remixapi_BridgeCallback endSceneCallback, + PFN_remixapi_BridgeCallback presentCallback +); + +#ifdef __cplusplus +namespace remixapi { + +namespace exported_func_name { +static constexpr char initRemixApi[] = "remixapi_InitializeLibrary"; +static constexpr char registerCallbacks[] = "remixapi_RegisterCallbacks"; +} + +inline remixapi_ErrorCode bridge_initRemixApi(remixapi_Interface* out_remixInterface) { + HMODULE hModule = GetModuleHandleA("d3d9.dll"); + if (hModule) { + PFN_remixapi_InitializeLibrary const pfn_Initialize = + (PFN_remixapi_InitializeLibrary)GetProcAddress(hModule, exported_func_name::initRemixApi); + if (!pfn_Initialize) { + return REMIXAPI_ERROR_CODE_GET_PROC_ADDRESS_FAILURE; + } + + const remixapi_InitializeLibraryInfo initInfo{ + REMIXAPI_STRUCT_TYPE_INITIALIZE_LIBRARY_INFO, + nullptr, + REMIXAPI_VERSION_MAKE(REMIXAPI_VERSION_MAJOR,REMIXAPI_VERSION_MINOR,REMIXAPI_VERSION_PATCH)}; + remixapi_Interface remixInterface = { 0 }; + const remixapi_ErrorCode status = pfn_Initialize(&initInfo, &remixInterface); + if (status == REMIXAPI_ERROR_CODE_SUCCESS) { + *out_remixInterface = remixInterface; + } + return status; + } + return REMIXAPI_ERROR_CODE_LOAD_LIBRARY_FAILURE; +} + +inline remixapi_ErrorCode bridge_setRemixApiCallbacks( + PFN_remixapi_BridgeCallback beginSceneCallback = nullptr, + PFN_remixapi_BridgeCallback endSceneCallback = nullptr, + PFN_remixapi_BridgeCallback presentCallback = nullptr) { + + HMODULE hModule = GetModuleHandleA("d3d9.dll"); + if (hModule) { + PFN_remixapi_RegisterCallbacks const pfn_RegisterCallbacks = + (PFN_remixapi_RegisterCallbacks)GetProcAddress(hModule, exported_func_name::registerCallbacks); + if (!pfn_RegisterCallbacks) { + return REMIXAPI_ERROR_CODE_GET_PROC_ADDRESS_FAILURE; + } + const remixapi_ErrorCode status = pfn_RegisterCallbacks( + beginSceneCallback, endSceneCallback, presentCallback); + return status; + } + return REMIXAPI_ERROR_CODE_LOAD_LIBRARY_FAILURE; +} + +} +#endif diff --git a/src/api/remix_api/bridge_c.h b/src/api/remix_api/bridge_c.h deleted file mode 100644 index ac58d71..0000000 --- a/src/api/remix_api/bridge_c.h +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. - * - * 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. - */ - -// Bridge API header for both the x86 bridge client and the x86 game - -#ifndef BRIDGE_C_H_ -#define BRIDGE_C_H_ - -#include -#include - -#define BRIDGEAPI_CALL __stdcall -#define BRIDGEAPI_PTR BRIDGEAPI_CALL - -#ifdef BRIDGE_API_IMPORT - #define BRIDGE_API __declspec(dllimport) -#else - #define BRIDGE_API __declspec(dllexport) -#endif - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - - typedef enum bridgeapi_ErrorCode { - BRIDGEAPI_ERROR_CODE_SUCCESS = 0, - BRIDGEAPI_ERROR_CODE_GENERAL_FAILURE = 1, - // WinAPI's GetModuleHandle has failed - BRIDGEAPI_ERROR_CODE_GET_MODULE_HANDLE_FAILURE = 2, - BRIDGEAPI_ERROR_CODE_INVALID_ARGUMENTS = 3, - // Couldn't find 'remixInitialize' function in the .dll - BRIDGEAPI_ERROR_CODE_GET_PROC_ADDRESS_FAILURE = 4, - // CreateD3D9 / RegisterD3D9Device can be called only once - BRIDGEAPI_ERROR_CODE_ALREADY_EXISTS = 5, - // RegisterD3D9Device requires the device that was created with IDirect3DDevice9Ex, returned by CreateD3D9 - BRIDGEAPI_ERROR_CODE_REGISTERING_NON_REMIX_D3D9_DEVICE = 6, - // RegisterD3D9Device was not called - BRIDGEAPI_ERROR_CODE_REMIX_DEVICE_WAS_NOT_REGISTERED = 7, - BRIDGEAPI_ERROR_CODE_INCOMPATIBLE_VERSION = 8, - // WinAPI's SetDllDirectory has failed - //BRIDGEAPI_ERROR_CODE_SET_DLL_DIRECTORY_FAILURE = 9, - // WinAPI's GetFullPathName has failed - //BRIDGEAPI_ERROR_CODE_GET_FULL_PATH_NAME_FAILURE = 10, - BRIDGEAPI_ERROR_CODE_NOT_INITIALIZED = 11, - } BRIDGEAPI_ErrorCode; - - // ------------------------------------------ - // <- remix api types from the remix_c header - - typedef enum remixapi_StructType { - REMIXAPI_STRUCT_TYPE_NONE = 0, - REMIXAPI_STRUCT_TYPE_INITIALIZE_LIBRARY_INFO = 1, - REMIXAPI_STRUCT_TYPE_MATERIAL_INFO = 2, - REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_PORTAL_EXT = 3, - REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_TRANSLUCENT_EXT = 4, - REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_OPAQUE_EXT = 5, - REMIXAPI_STRUCT_TYPE_LIGHT_INFO = 6, - REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DISTANT_EXT = 7, - REMIXAPI_STRUCT_TYPE_LIGHT_INFO_CYLINDER_EXT = 8, - REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DISK_EXT = 9, - REMIXAPI_STRUCT_TYPE_LIGHT_INFO_RECT_EXT = 10, - REMIXAPI_STRUCT_TYPE_LIGHT_INFO_SPHERE_EXT = 11, - REMIXAPI_STRUCT_TYPE_MESH_INFO = 12, - REMIXAPI_STRUCT_TYPE_INSTANCE_INFO = 13, - REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_BONE_TRANSFORMS_EXT = 14, - REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_BLEND_EXT = 15, - REMIXAPI_STRUCT_TYPE_CAMERA_INFO = 16, - REMIXAPI_STRUCT_TYPE_CAMERA_INFO_PARAMETERIZED_EXT = 17, - REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_OPAQUE_SUBSURFACE_EXT = 18, - REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_OBJECT_PICKING_EXT = 19, - REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DOME_EXT = 20, - REMIXAPI_STRUCT_TYPE_LIGHT_INFO_USD_EXT = 21, - REMIXAPI_STRUCT_TYPE_STARTUP_INFO = 22, - REMIXAPI_STRUCT_TYPE_PRESENT_INFO = 23, - // NOTE: if adding a new struct, register it in 'rtx_remix_specialization.inl' - } remixapi_StructType; - - namespace x86 - { - typedef uint32_t remixapi_Bool; - - typedef struct remixapi_Rect2D { - int32_t left; - int32_t top; - int32_t right; - int32_t bottom; - } remixapi_Rect2D; - - typedef struct remixapi_Float2D { - float x; - float y; - } remixapi_Float2D; - - typedef struct remixapi_Float3D { - float x; - float y; - float z; - } remixapi_Float3D; - - typedef struct remixapi_Float4D { - float x; - float y; - float z; - float w; - } remixapi_Float4D; - - typedef struct remixapi_Transform { - float matrix[3][4]; - } remixapi_Transform; - - //typedef struct remixapi_MaterialHandle_T* remixapi_MaterialHandle; - //typedef struct remixapi_MeshHandle_T* remixapi_MeshHandle; - //typedef struct remixapi_LightHandle_T* remixapi_LightHandle; - - typedef const wchar_t* remixapi_Path; - - typedef struct remixapi_MaterialInfoOpaqueEXT { - remixapi_StructType sType; - //void* pNext; - remixapi_Path roughnessTexture; - remixapi_Path metallicTexture; - float anisotropy; - remixapi_Float3D albedoConstant; - float opacityConstant; - float roughnessConstant; - float metallicConstant; - remixapi_Bool thinFilmThickness_hasvalue; - float thinFilmThickness_value; - remixapi_Bool alphaIsThinFilmThickness; - remixapi_Path heightTexture; - float heightTextureStrength; - // If true, InstanceInfoBlendEXT is used as a source for alpha state - remixapi_Bool useDrawCallAlphaState; - remixapi_Bool blendType_hasvalue; - int blendType_value; - remixapi_Bool invertedBlend; - int alphaTestType; - uint8_t alphaReferenceValue; - } remixapi_MaterialInfoOpaqueEXT; - - // Valid only if remixapi_MaterialInfo contains remixapi_MaterialInfoOpaqueEXT in pNext chain - typedef struct remixapi_MaterialInfoOpaqueSubsurfaceEXT { - remixapi_StructType sType; - //void* pNext; - remixapi_Path subsurfaceTransmittanceTexture; - remixapi_Path subsurfaceThicknessTexture; - remixapi_Path subsurfaceSingleScatteringAlbedoTexture; - remixapi_Float3D subsurfaceTransmittanceColor; - float subsurfaceMeasurementDistance; - remixapi_Float3D subsurfaceSingleScatteringAlbedo; - float subsurfaceVolumetricAnisotropy; - } remixapi_MaterialInfoOpaqueSubsurfaceEXT; - - typedef struct remixapi_MaterialInfoTranslucentEXT { - remixapi_StructType sType; - //void* pNext; - remixapi_Path transmittanceTexture; - float refractiveIndex; - remixapi_Float3D transmittanceColor; - float transmittanceMeasurementDistance; - remixapi_Bool thinWallThickness_hasvalue; - float thinWallThickness_value; - remixapi_Bool useDiffuseLayer; - } remixapi_MaterialInfoTranslucentEXT; - - typedef struct remixapi_MaterialInfoPortalEXT { - remixapi_StructType sType; - //void* pNext; - uint8_t rayPortalIndex; - float rotationSpeed; - } remixapi_MaterialInfoPortalEXT; - - typedef struct remixapi_MaterialInfo { - remixapi_StructType sType; - //void* pNext; - uint64_t hash; - remixapi_Path albedoTexture; - remixapi_Path normalTexture; - remixapi_Path tangentTexture; - remixapi_Path emissiveTexture; - float emissiveIntensity; - remixapi_Float3D emissiveColorConstant; - uint8_t spriteSheetRow; - uint8_t spriteSheetCol; - uint8_t spriteSheetFps; - uint8_t filterMode; // Nearest: 0 Linear: 1 - uint8_t wrapModeU; // Clamp: 0 Repeat: 1 Mirrored_Repeat: 2 Clip: 3 - uint8_t wrapModeV; // Clamp: 0 Repeat: 1 Mirrored_Repeat: 2 Clip: 3 - } remixapi_MaterialInfo; - - - typedef struct remixapi_HardcodedVertex { - float position[3]; - float normal[3]; - float texcoord[2]; - uint32_t color; - uint32_t _pad0; - uint32_t _pad1; - uint32_t _pad2; - uint32_t _pad3; - uint32_t _pad4; - uint32_t _pad5; - uint32_t _pad6; - } remixapi_HardcodedVertex; - - // # TODO remixapi_MeshInfoSkinning - - typedef struct remixapi_MeshInfoSurfaceTriangles { - const remixapi_HardcodedVertex* vertices_values; - uint64_t vertices_count; - const uint32_t* indices_values; - uint64_t indices_count; - remixapi_Bool skinning_hasvalue; - //remixapi_MeshInfoSkinning skinning_value; // # TODO - uint64_t material; // CHANGED - was remixapi_MaterialHandle - } remixapi_MeshInfoSurfaceTriangles; - - typedef struct remixapi_MeshInfo { - remixapi_StructType sType; - //void* pNext; - uint64_t hash; - const remixapi_MeshInfoSurfaceTriangles* surfaces_values; - uint32_t surfaces_count; - } remixapi_MeshInfo; - - - typedef struct remixapi_LightInfoLightShaping { - // The direction the Light Shaping is pointing in. Must be normalized. - remixapi_Float3D direction; - float coneAngleDegrees; - float coneSoftness; - float focusExponent; - } remixapi_LightInfoLightShaping; - - typedef struct remixapi_LightInfoSphereEXT { - remixapi_StructType sType; - //void* pNext; - remixapi_Float3D position; - float radius; - remixapi_Bool shaping_hasvalue; - remixapi_LightInfoLightShaping shaping_value; - } remixapi_LightInfoSphereEXT; - - typedef struct remixapi_LightInfoRectEXT { - remixapi_StructType sType; - //void* pNext; - remixapi_Float3D position; - // The X axis of the Rect Light. Must be normalized and orthogonal to the Y and direction axes. - remixapi_Float3D xAxis; - float xSize; - // The Y axis of the Rect Light. Must be normalized and orthogonal to the X and direction axes. - remixapi_Float3D yAxis; - float ySize; - // The direction the Rect Light is pointing in, should match the Shaping direction if present. - // Must be normalized and orthogonal to the X and Y axes. - remixapi_Float3D direction; - remixapi_Bool shaping_hasvalue; - remixapi_LightInfoLightShaping shaping_value; - } remixapi_LightInfoRectEXT; - - typedef struct remixapi_LightInfoDiskEXT { - remixapi_StructType sType; - //void* pNext; - remixapi_Float3D position; - // The X axis of the Disk Light. Must be normalized and orthogonal to the Y and direction axes. - remixapi_Float3D xAxis; - float xRadius; - // The Y axis of the Disk Light. Must be normalized and orthogonal to the X and direction axes. - remixapi_Float3D yAxis; - float yRadius; - // The direction the Disk Light is pointing in, should match the Shaping direction if present - // Must be normalized and orthogonal to the X and Y axes. - remixapi_Float3D direction; - remixapi_Bool shaping_hasvalue; - remixapi_LightInfoLightShaping shaping_value; - } remixapi_LightInfoDiskEXT; - - typedef struct remixapi_LightInfoCylinderEXT { - remixapi_StructType sType; - //void* pNext; - remixapi_Float3D position; - float radius; - // The "center" axis of the Cylinder Light. Must be normalized. - remixapi_Float3D axis; - float axisLength; - } remixapi_LightInfoCylinderEXT; - - typedef struct remixapi_LightInfoDistantEXT { - remixapi_StructType sType; - //void* pNext; - // The direction the Distant Light is pointing in. Must be normalized. - remixapi_Float3D direction; - float angularDiameterDegrees; - } remixapi_LightInfoDistantEXT; - - typedef struct remixapi_LightInfo { - remixapi_StructType sType; - //void* pNext; - uint64_t hash; - remixapi_Float3D radiance; - } remixapi_LightInfo; - } - - // -------------------------------------------- - // remix api types end -> - - typedef void(BRIDGEAPI_PTR* PFN_bridgeapi_DebugPrint)(const char* text); - typedef uint64_t(BRIDGEAPI_PTR* PFN_bridgeapi_CreateOpaqueMaterial)(const x86::remixapi_MaterialInfo* info, const x86::remixapi_MaterialInfoOpaqueEXT* ext, const x86::remixapi_MaterialInfoOpaqueSubsurfaceEXT* ext_ss); - typedef uint64_t(BRIDGEAPI_PTR* PFN_bridgeapi_CreateTranslucentMaterial)(const x86::remixapi_MaterialInfo* info, const x86::remixapi_MaterialInfoTranslucentEXT* ext); - typedef uint64_t(BRIDGEAPI_PTR* PFN_bridgeapi_CreatePortalMaterial)(const x86::remixapi_MaterialInfo* info, const x86::remixapi_MaterialInfoPortalEXT* ext); - typedef void(BRIDGEAPI_PTR* PFN_bridgeapi_DestroyMaterial)(uint64_t handle); - typedef uint64_t(BRIDGEAPI_PTR* PFN_bridgeapi_CreateTriangleMesh)(const x86::remixapi_MeshInfo* info); - typedef void(BRIDGEAPI_PTR* PFN_bridgeapi_DestroyMesh)(uint64_t handle); - typedef void(BRIDGEAPI_PTR* PFN_bridgeapi_DrawMeshInstance)(uint64_t handle, const x86::remixapi_Transform* t, x86::remixapi_Bool double_sided); - typedef uint64_t(BRIDGEAPI_PTR* PFN_bridgeapi_CreateSphereLight)(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoSphereEXT* ext); - typedef uint64_t(BRIDGEAPI_PTR* PFN_bridgeapi_CreateRectLight)(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoRectEXT* ext); - typedef uint64_t(BRIDGEAPI_PTR* PFN_bridgeapi_CreateDiskLight)(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoDiskEXT* ext); - typedef uint64_t(BRIDGEAPI_PTR* PFN_bridgeapi_CreateCylinderLight)(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoCylinderEXT* ext); - typedef uint64_t(BRIDGEAPI_PTR* PFN_bridgeapi_CreateDistantLight)(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoDistantEXT* ext); - typedef void(BRIDGEAPI_PTR* PFN_bridgeapi_DestroyLight)(uint64_t handle); - typedef void(BRIDGEAPI_PTR* PFN_bridgeapi_DrawLightInstance)(uint64_t handle); - typedef void(BRIDGEAPI_PTR* PFN_bridgeapi_SetConfigVariable)(const char* var, const char* value); - typedef void(BRIDGEAPI_PTR* PFN_bridgeapi_RegisterDevice)(void); - typedef void(__cdecl* PFN_bridgeapi_RegisterEndSceneCallback)(void); - - typedef struct bridgeapi_Interface { - bool initialized; - PFN_bridgeapi_DebugPrint DebugPrint; // const char* text - PFN_bridgeapi_CreateOpaqueMaterial CreateOpaqueMaterial; // x86::remixapi_MaterialInfo* - PFN_bridgeapi_CreateTranslucentMaterial CreateTranslucentMaterial; // x86::remixapi_MaterialInfo* --- x86::remixapi_MaterialInfoTranslucentEXT* - PFN_bridgeapi_CreatePortalMaterial CreatePortalMaterial; // x86::remixapi_MaterialInfo* --- x86::remixapi_MaterialInfoPortalEXT* - PFN_bridgeapi_DestroyMaterial DestroyMaterial; // uint64_t handle - PFN_bridgeapi_CreateTriangleMesh CreateTriangleMesh; // x86::remixapi_MeshInfo* - PFN_bridgeapi_DestroyMesh DestroyMesh; // uint64_t handle - PFN_bridgeapi_DrawMeshInstance DrawMeshInstance; // uint64_t handle --- x86::remixapi_Transform* t --- x86::remixapi_Bool double_sided - PFN_bridgeapi_CreateSphereLight CreateSphereLight; // x86::remixapi_LightInfo* --- x86::remixapi_LightInfoSphereEXT* - PFN_bridgeapi_CreateRectLight CreateRectLight; // x86::remixapi_LightInfo* --- x86::remixapi_LightInfoRectEXT* - PFN_bridgeapi_CreateDiskLight CreateDiskLight; // x86::remixapi_LightInfo* --- x86::remixapi_LightInfoDiskEXT* - PFN_bridgeapi_CreateCylinderLight CreateCylinderLight; // x86::remixapi_LightInfo* --- x86::remixapi_LightInfoCylinderEXT* - PFN_bridgeapi_CreateDistantLight CreateDistantLight; // x86::remixapi_LightInfo* --- x86::remixapi_LightInfoDistantEXT* - PFN_bridgeapi_DestroyLight DestroyLight; // uint64_t handle - PFN_bridgeapi_DrawLightInstance DrawLightInstance; // uint64_t handle - PFN_bridgeapi_SetConfigVariable SetConfigVariable; // const char* var --- const char* value - PFN_bridgeapi_RegisterDevice RegisterDevice; // void - void (*RegisterEndSceneCallback)(PFN_bridgeapi_RegisterEndSceneCallback callback); - } bridgeapi_Interface; - - BRIDGE_API BRIDGEAPI_ErrorCode __cdecl bridgeapi_InitFuncs(bridgeapi_Interface* out_result); - typedef BRIDGEAPI_ErrorCode(__cdecl* PFN_bridgeapi_InitFuncs)(bridgeapi_Interface* out_result); - - // -------- - // -------- - - inline BRIDGEAPI_ErrorCode bridgeapi_initialize(bridgeapi_Interface* out_bridgeInterface) { - - PFN_bridgeapi_InitFuncs pfn_Initialize = nullptr; - HMODULE hModule = GetModuleHandleA("d3d9.dll"); - if (hModule) { - PROC func = GetProcAddress(hModule, "bridgeapi_InitFuncs"); - if (func) { - pfn_Initialize = (PFN_bridgeapi_InitFuncs) func; - } - else { - return BRIDGEAPI_ERROR_CODE_GET_PROC_ADDRESS_FAILURE; - } - - bridgeapi_Interface bridgeInterface = { 0 }; - bridgeapi_ErrorCode status = pfn_Initialize(&bridgeInterface); - if (status != BRIDGEAPI_ERROR_CODE_SUCCESS) { - return status; - } - - bridgeInterface.initialized = true; - *out_bridgeInterface = bridgeInterface; - - return status; - } - return BRIDGEAPI_ERROR_CODE_GET_MODULE_HANDLE_FAILURE; - } - -#ifdef __cplusplus -} -#endif // __cplusplus - -#endif // BRIDGE_C_H_ diff --git a/src/client/client_options.h b/src/client/client_options.h index 551b2b0..ec2146c 100644 --- a/src/client/client_options.h +++ b/src/client/client_options.h @@ -67,10 +67,6 @@ namespace ClientOptions { return bridge_util::Config::getOption("client.enableDpiAwareness", true); } - inline bool getExposeRemixApi() { - return bridge_util::Config::getOption("client.exposeRemixApi", false); - } - // If set, the space for data for dynamic buffer updates will be preallocated on data channel // and redundant copy will be avioded. However, because D3D applications are not obliged // to write the entire locked region this optimization is NOT considered safe and may diff --git a/src/client/d3d9_device.cpp b/src/client/d3d9_device.cpp index d7a54b1..3cacf18 100644 --- a/src/client/d3d9_device.cpp +++ b/src/client/d3d9_device.cpp @@ -39,6 +39,7 @@ #include "client_options.h" #include "swapchain_map.h" #include "config/global_options.h" +#include "remix_api.h" #include "util_bridge_assert.h" #include "util_semaphore.h" @@ -46,8 +47,6 @@ #include #include - #include "remix_api.h" - #define GET_PRES_PARAM() (m_pSwapchain->getPresentationParameters()) #define SetShaderConst(func, StartRegister, pConstantData, Count, size, currentUID) \ @@ -495,6 +494,10 @@ HRESULT Direct3DDevice9Ex_LSS::Present(CONST RECT* pSourceRect, CONS return hresult; } + if(remixapi::g_bInterfaceInitialized && remixapi::g_presentCallback) { + remixapi::g_presentCallback(); + } + // Seeing this in the log could indicate the game is sending inputs to a different window if(GetWindowLongPtr(getWinProcHwnd(),GWLP_WNDPROC) != reinterpret_cast(RemixWndProc)) { setWinProc(getWinProcHwnd(), true); @@ -1098,6 +1101,10 @@ HRESULT Direct3DDevice9Ex_LSS::BeginScene() { gSceneState = SceneInProgress; } } + + if (remixapi::g_bInterfaceInitialized && remixapi::g_beginSceneCallback) { + remixapi::g_beginSceneCallback(); + } UID currentUID = 0; { @@ -1119,8 +1126,8 @@ HRESULT Direct3DDevice9Ex_LSS::EndScene() { } } - if (remix_api::interfaceInitialized && remix_api::interfaceGameCallback) { - remix_api::interfaceGameCallback(); + if (remixapi::g_bInterfaceInitialized && remixapi::g_endSceneCallback) { + remixapi::g_endSceneCallback(); } UID currentUID = 0; @@ -1202,9 +1209,15 @@ HRESULT Direct3DDevice9Ex_LSS::SetTransform(D3DTRANSFORMSTATETYPE St { BRIDGE_DEVICE_LOCKGUARD(); if (m_stateRecording) { + if (memcmp(&m_stateRecording->m_captureState.transforms[idx], pMatrix, sizeof(D3DMATRIX)) == 0) { + return S_OK; + } m_stateRecording->m_captureState.transforms[idx] = *pMatrix; m_stateRecording->m_dirtyFlags.transforms[idx] = true; } else { + if (memcmp(&m_state.transforms[idx], pMatrix, sizeof(D3DMATRIX)) == 0) { + return S_OK; + } m_state.transforms[idx] = *pMatrix; } } @@ -1373,9 +1386,15 @@ HRESULT Direct3DDevice9Ex_LSS::SetLight(DWORD Index, CONST D3DLIGHT9 { BRIDGE_DEVICE_LOCKGUARD(); if (m_stateRecording) { + if (memcmp(&m_stateRecording->m_captureState.lights[Index], pLight, sizeof(D3DLIGHT9)) == 0) { + return S_OK; + } m_stateRecording->m_captureState.lights[Index] = *pLight; m_stateRecording->m_dirtyFlags.lights[Index] = true; } else { + if (memcmp(&m_state.lights[Index], pLight, sizeof(D3DLIGHT9)) == 0) { + return S_OK; + } m_state.lights[Index] = *pLight; } } @@ -1415,8 +1434,14 @@ HRESULT Direct3DDevice9Ex_LSS::LightEnable(DWORD LightIndex, BOOL bE { BRIDGE_DEVICE_LOCKGUARD(); if (m_stateRecording) { + if (m_stateRecording->m_captureState.bLightEnables[LightIndex] == (bool)bEnable) { + return S_OK; + } m_stateRecording->m_captureState.bLightEnables[LightIndex] = bEnable; } else { + if (m_state.bLightEnables[LightIndex] == (bool)bEnable) { + return S_OK; + } m_state.bLightEnables[LightIndex] = bEnable; } } @@ -1508,9 +1533,15 @@ HRESULT Direct3DDevice9Ex_LSS::SetRenderState(D3DRENDERSTATETYPE Sta { BRIDGE_DEVICE_LOCKGUARD(); if (m_stateRecording) { + if (m_stateRecording->m_captureState.renderStates[State] == Value) { + return S_OK; + } m_stateRecording->m_captureState.renderStates[State] = Value; m_stateRecording->m_dirtyFlags.renderStates[State] = true; } else { + if (m_state.renderStates[State] == Value) { + return S_OK; + } m_state.renderStates[State] = Value; } } @@ -2046,10 +2077,17 @@ HRESULT Direct3DDevice9Ex_LSS::SetTextureStageState(DWORD Stage, D3D { BRIDGE_DEVICE_LOCKGUARD(); if (m_stateRecording) { + if (m_stateRecording->m_captureState.textureStageStates[stageIdx][typeIdx] == Value) { + return S_OK; + } m_stateRecording->m_captureState.textureStageStates[stageIdx][typeIdx] = Value; m_stateRecording->m_dirtyFlags.textureStageStates[stageIdx][typeIdx] = true; + } else { + if (m_state.textureStageStates[stageIdx][typeIdx] == Value) { + return S_OK; + } + m_state.textureStageStates[stageIdx][typeIdx] = Value; } - m_state.textureStageStates[stageIdx][typeIdx] = Value; } { ClientMessage c(Commands::IDirect3DDevice9Ex_SetTextureStageState, getId()); @@ -2096,9 +2134,15 @@ HRESULT Direct3DDevice9Ex_LSS::SetSamplerState(DWORD Sampler, D3DSAM { BRIDGE_DEVICE_LOCKGUARD(); if (m_stateRecording) { + if (m_stateRecording->m_captureState.samplerStates[samplerIdx][typeIdx] == Value) { + return S_OK; + } m_stateRecording->m_captureState.samplerStates[samplerIdx][typeIdx] = Value; m_stateRecording->m_dirtyFlags.samplerStates[samplerIdx][typeIdx] = true; } else { + if (m_state.samplerStates[samplerIdx][typeIdx] == Value) { + return S_OK; + } m_state.samplerStates[samplerIdx][typeIdx] = Value; } } @@ -2414,10 +2458,6 @@ HRESULT Direct3DDevice9Ex_LSS::SetVertexDeclaration(IDirect3DVertexD ZoneScoped; LogFunctionCall(); - if (pDecl == nullptr) { - return D3DERR_INVALIDCALL; - } - auto* const pLssVtxDecl = bridge_cast(pDecl); const UID id = (pLssVtxDecl) ? (UID) pLssVtxDecl->getId() : 0; UID currentUID = 0; diff --git a/src/client/d3d9_lss.cpp b/src/client/d3d9_lss.cpp index 8465116..0004614 100644 --- a/src/client/d3d9_lss.cpp +++ b/src/client/d3d9_lss.cpp @@ -151,9 +151,8 @@ void OnServerExited(Process const* process) { gbBridgeRunning = false; // Notify the user that we have to shut down the bridge entirely because we don't have a renderer anymore if (BridgeState::getClientState() != BridgeState::ProcessState::DoneProcessing) { - Logger::err("The bridge server process has unexpectedly exited, closing the bridge client and host application."); PrintRecentCommandHistory(); - MessageBox(nullptr, "The bridge server process has unexpectedly exited, closing the bridge client and host application.", "RTX Remix Bridge Error!",MB_OK); + Logger::errLogMessageBoxAndExit(logger_strings::BridgeClientClosing); std::abort(); } diff --git a/src/client/d3d9_lss.def b/src/client/d3d9_lss.def index 59e23a6..0fbd289 100644 --- a/src/client/d3d9_lss.def +++ b/src/client/d3d9_lss.def @@ -17,3 +17,6 @@ EXPORTS Direct3DCreate9Ex @ 38 DetourFinishHelperProcess @ 1 + + remixapi_InitializeLibrary @ 39 + remixapi_RegisterCallbacks @ 40 diff --git a/src/client/d3d9_module.cpp b/src/client/d3d9_module.cpp index b3258ae..175430f 100644 --- a/src/client/d3d9_module.cpp +++ b/src/client/d3d9_module.cpp @@ -114,7 +114,7 @@ UINT Direct3D9Ex_LSS::GetAdapterCount() { ModuleClientCommand c(Commands::IDirect3D9Ex_GetAdapterCount); currentUID = c.get_uid(); } - WAIT_FOR_SERVER_RESPONSE("GetAdapterCount()", 0, currentUID); + WAIT_FOR_SERVER_RESPONSE("GetAdapterCount()", 1, currentUID); m_adapterCount = (UINT) ModuleBridge::get_data(); ModuleBridge::pop_front(); diff --git a/src/client/meson.build b/src/client/meson.build index e1bf496..732439b 100644 --- a/src/client/meson.build +++ b/src/client/meson.build @@ -82,7 +82,6 @@ d3d9_header = files([ 'framework.h', 'lockable_buffer.h', 'pch.h', - 'remix_api.h', 'remix_state.h', 'resource.h', 'shadow_map.h', @@ -93,14 +92,13 @@ d3d9_def = files([ ]) detours_include_path = include_directories('../../ext/Detours/src') -remixapi_include_path = include_directories('../api') thread_dep = dependency('threads') d3d9_dll = shared_library('d3d9', d3d9_src, d3d9_res, d3d9_header, sources : [ bridge_version ], dependencies : [ thread_dep, util_dep, lib_version, lib_version, lib_commCtrl, tracy_dep ], - include_directories : [ bridge_include_path, util_include_path, detours_include_path, remixapi_include_path ], + include_directories : [ bridge_include_path, util_include_path, detours_include_path, public_include_path, ext_include_path ], install : true, vs_module_defs : 'd3d9_lss.def') diff --git a/src/client/remix_api.cpp b/src/client/remix_api.cpp index 9ded96c..3d39f09 100644 --- a/src/client/remix_api.cpp +++ b/src/client/remix_api.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -23,485 +23,454 @@ #include "log/log.h" #include "util_bridgecommand.h" #include "util_devicecommand.h" -#include "remix_api.h" +#include "util_remixapi.h" +using namespace remixapi::util; -#define SEND_FLOAT(MSG, V) \ - (MSG).send_data((uint32_t) *(uint32_t*)(&(V))) +namespace remixapi { -#define SEND_FLOAT2D(MSG, V) \ - SEND_FLOAT(MSG, (V).x); \ - SEND_FLOAT(MSG, (V).y) +bool g_bInterfaceInitialized = false; +PFN_remixapi_BridgeCallback g_beginSceneCallback = nullptr; +PFN_remixapi_BridgeCallback g_endSceneCallback = nullptr; +PFN_remixapi_BridgeCallback g_presentCallback = nullptr; -#define SEND_FLOAT3D(MSG, V) \ - SEND_FLOAT(MSG, (V).x); \ - SEND_FLOAT(MSG, (V).y); \ - SEND_FLOAT(MSG, (V).z) +template +inline void send(ClientMessage& msg, const T& val) { + msg.send_data(sizeof(T), &val); +} -#define SEND_FLOAT4D(MSG, V) \ - SEND_FLOAT(MSG, (V).x); \ - SEND_FLOAT(MSG, (V).y); \ - SEND_FLOAT(MSG, (V).z); \ - SEND_FLOAT(MSG, (V).w) +template<> +inline void send(ClientMessage& msg, const remixapi_Float3D& vec3) { + send(msg, vec3.x); + send(msg, vec3.y); + send(msg, vec3.z); +} -#define SEND_INT(MSG, T) \ - (MSG).send_data((int32_t) (T)) +template<> +inline void send(ClientMessage& msg, const remixapi_Path& path) { + const auto nonNullPath = (path) ? (path) : L""; + msg.send_data(wcslen(nonNullPath) * sizeof(wchar_t), nonNullPath); +} -#define SEND_STYPE(MSG, T) \ - (MSG).send_data((uint32_t) (T)) +template<> +inline void send(ClientMessage& msg, const char* const& c_str) { + msg.send_data((uint32_t) strlen(c_str) + 1, c_str); +} -#define SEND_U32(MSG, U32) \ - (MSG).send_data((uint32_t) (U32)) +template<> +inline void send(ClientMessage& msg, const HandleUID& handle) { + msg.send_data(handle.uid); +} -#define SEND_U64(MSG, U64) \ - (MSG).send_data(sizeof(uint64_t), &(U64)) +template<> +inline void send(ClientMessage& msg, const Bool& b) { + uint32_t boolVal = 0x0; + boolVal |= (uint8_t)b; + msg.send_data(boolVal); +} -#define SEND_PATH(MSG, PATH) \ - (MSG).send_data((uint32_t)wcslen((PATH) ? (PATH) : L"") * sizeof(wchar_t), (PATH) ? (PATH) : L"") +template +auto serializeAndSend(ClientMessage& msg, const SerializableT& serializable) { + static_assert(is_serializable_v, "serializeAndSend(...) may only be called with defined Serializable types"); + msg.send_data(ToRemixApiStructEnum); + const auto serializableSize = serializable.size(); + auto pSlzd = new uint8_t[serializableSize]; + serializable.serialize(pSlzd); + msg.send_data(serializableSize, pSlzd); + delete pSlzd; +} -#define PULL_DATA(SIZE, NAME) \ - uint32_t NAME##_len = DeviceBridge::get_data((void**)&(NAME)); \ - assert(NAME##_len == 0 || (SIZE) == NAME##_len) -namespace remix_api { - bool interfaceInitialized = false; - PFN_bridgeapi_RegisterEndSceneCallback interfaceGameCallback = nullptr; -} +remixapi_ErrorCode REMIXAPI_CALL remixapi_CreateMaterial( + const remixapi_MaterialInfo* info, + remixapi_MaterialHandle* out_handle) { -namespace { - void BRIDGEAPI_CALL bridgeapi_DebugPrint(const char* text) { - if (text) { - ClientMessage c(Commands::Api_DebugPrint); - c.send_data((uint32_t) strlen(text), text); - } - } + ASSERT_REMIXAPI_PFN_TYPE(remixapi_CreateMaterial); + assert(info->sType == REMIXAPI_STRUCT_TYPE_MATERIAL_INFO); - uint64_t BRIDGEAPI_CALL bridgeapi_CreateOpaqueMaterial(const x86::remixapi_MaterialInfo* info, const x86::remixapi_MaterialInfoOpaqueEXT* ext, const x86::remixapi_MaterialInfoOpaqueSubsurfaceEXT* ext_ss) { - UID currentUID = 0; - { - ClientMessage c(Commands::Api_CreateOpaqueMaterial); - currentUID = c.get_uid(); - - // MaterialInfo - SEND_STYPE(c, info->sType); - SEND_U64(c, info->hash); - SEND_PATH(c, info->albedoTexture); - SEND_PATH(c, info->normalTexture); - SEND_PATH(c, info->tangentTexture); - SEND_PATH(c, info->emissiveTexture); - SEND_FLOAT(c, info->emissiveIntensity); - SEND_FLOAT3D(c, info->emissiveColorConstant); - c.send_data((uint8_t) info->spriteSheetRow); - c.send_data((uint8_t) info->spriteSheetCol); - c.send_data((uint8_t) info->spriteSheetFps); - c.send_data((uint8_t) info->filterMode); - c.send_data((uint8_t) info->wrapModeU); - c.send_data((uint8_t) info->wrapModeV); - - // MaterialInfoOpaqueEXT - SEND_STYPE(c, ext->sType); - SEND_PATH(c, ext->roughnessTexture); - SEND_PATH(c, ext->metallicTexture); - SEND_FLOAT(c, ext->anisotropy); - SEND_FLOAT3D(c, ext->albedoConstant); - SEND_FLOAT(c, ext->opacityConstant); - SEND_FLOAT(c, ext->roughnessConstant); - SEND_FLOAT(c, ext->metallicConstant); - SEND_U32(c, ext->thinFilmThickness_hasvalue); - SEND_FLOAT(c, ext->thinFilmThickness_value); - SEND_U32(c, ext->alphaIsThinFilmThickness); - SEND_PATH(c, ext->heightTexture); - SEND_FLOAT(c, ext->heightTextureStrength); - SEND_U32(c, ext->useDrawCallAlphaState); // If true, InstanceInfoBlendEXT is used as a source for alpha state - SEND_U32(c, ext->blendType_hasvalue); - SEND_INT(c, ext->blendType_value); - SEND_U32(c, ext->invertedBlend); - SEND_INT(c, ext->alphaTestType); - c.send_data((uint8_t) ext->alphaReferenceValue); - - const x86::remixapi_Bool has_ss = ext_ss ? TRUE : FALSE; - SEND_U32(c, has_ss); - - if (ext_ss) { - // MaterialInfoOpaqueSubsurfaceEXT - SEND_STYPE(c, ext_ss->sType); - SEND_PATH(c, ext_ss->subsurfaceTransmittanceTexture); - SEND_PATH(c, ext_ss->subsurfaceThicknessTexture); - SEND_PATH(c, ext_ss->subsurfaceSingleScatteringAlbedoTexture); - SEND_FLOAT3D(c, ext_ss->subsurfaceTransmittanceColor); - SEND_FLOAT(c, ext_ss->subsurfaceMeasurementDistance); - SEND_FLOAT3D(c, ext_ss->subsurfaceSingleScatteringAlbedo); - SEND_FLOAT(c, ext_ss->subsurfaceVolumetricAnisotropy); - } - } + HandleUID newHandle; + { + ClientMessage c(Commands::RemixApi_CreateMaterial); - WAIT_FOR_SERVER_RESPONSE("CreateMaterial()", 0, currentUID); - uint64_t* result = nullptr; - PULL_DATA(sizeof(uint64_t), result); - DeviceBridge::pop_front(); - return *result; - } + serializeAndSend(c, *info); - uint64_t BRIDGEAPI_CALL bridgeapi_CreateTranslucentMaterial(const x86::remixapi_MaterialInfo* info, const x86::remixapi_MaterialInfoTranslucentEXT* ext) { - UID currentUID = 0; - { - ClientMessage c(Commands::Api_CreateTranslucentMaterial); - currentUID = c.get_uid(); - - // MaterialInfo - SEND_STYPE(c, info->sType); - SEND_U64(c, info->hash); - SEND_PATH(c, info->albedoTexture); - SEND_PATH(c, info->normalTexture); - SEND_PATH(c, info->tangentTexture); - SEND_PATH(c, info->emissiveTexture); - SEND_FLOAT(c, info->emissiveIntensity); - SEND_FLOAT3D(c, info->emissiveColorConstant); - c.send_data((uint8_t) info->spriteSheetRow); - c.send_data((uint8_t) info->spriteSheetCol); - c.send_data((uint8_t) info->spriteSheetFps); - c.send_data((uint8_t) info->filterMode); - c.send_data((uint8_t) info->wrapModeU); - c.send_data((uint8_t) info->wrapModeV); - - // MaterialInfoTranslucentEXT - SEND_STYPE(c, ext->sType); - SEND_PATH(c, ext->transmittanceTexture); - SEND_FLOAT(c, ext->refractiveIndex); - SEND_FLOAT3D(c, ext->transmittanceColor); - SEND_FLOAT(c, ext->transmittanceMeasurementDistance); - SEND_U32(c, ext->thinWallThickness_hasvalue); - SEND_FLOAT(c, ext->thinWallThickness_value); - SEND_U32(c, ext->useDiffuseLayer); + // For each valid pNext, we will send a true-valued bool to indicate that + // server must read another extension. If it reads false, it knows that it + // is done reading. + // send(c, Bool::True); -> CONTINUE + // send(c, Bool::False); -> STOP + const void* infoItr = info; + while (auto* const pNext = getPNext(infoItr)) { + infoItr = pNext; + switch (getSType(pNext)) { + case REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_OPAQUE_EXT: + { + auto* pOpaqueMat = static_cast(infoItr); + send(c, Bool::True); + serializeAndSend(c, *pOpaqueMat); + break; + } + case REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_OPAQUE_SUBSURFACE_EXT: + { + auto* pOpaqueSubsurfaceMat = static_cast(infoItr); + send(c, Bool::True); + serializeAndSend(c, *pOpaqueSubsurfaceMat); + break; + } + case REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_TRANSLUCENT_EXT: + { + auto* pTranslucentMat = static_cast(infoItr); + send(c, Bool::True); + serializeAndSend(c, *pTranslucentMat); + break; + } + case REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_PORTAL_EXT: + { + auto* pPortalMat = static_cast(infoItr); + send(c, Bool::True); + serializeAndSend(c, *pPortalMat); + break; + } + default: + { + Logger::warn("[remixapi_CreateMaterial] Unknown sType. Skipping."); + break; + } + } + infoItr = pNext; } - - WAIT_FOR_SERVER_RESPONSE("CreateMaterial()", 0, currentUID); - uint64_t* result = nullptr; - PULL_DATA(sizeof(uint64_t), result); - DeviceBridge::pop_front(); - return *result; + send(c, Bool::False); + send(c, newHandle); } - uint64_t BRIDGEAPI_CALL bridgeapi_CreatePortalMaterial(const x86::remixapi_MaterialInfo* info, const x86::remixapi_MaterialInfoPortalEXT* ext) { - UID currentUID = 0; - { - ClientMessage c(Commands::Api_CreatePortalMaterial); - currentUID = c.get_uid(); - - // MaterialInfo - SEND_STYPE(c, info->sType); - SEND_U64(c, info->hash); - SEND_PATH(c, info->albedoTexture); - SEND_PATH(c, info->normalTexture); - SEND_PATH(c, info->tangentTexture); - SEND_PATH(c, info->emissiveTexture); - SEND_FLOAT(c, info->emissiveIntensity); - SEND_FLOAT3D(c, info->emissiveColorConstant); - c.send_data((uint8_t) info->spriteSheetRow); - c.send_data((uint8_t) info->spriteSheetCol); - c.send_data((uint8_t) info->spriteSheetFps); - c.send_data((uint8_t) info->filterMode); - c.send_data((uint8_t) info->wrapModeU); - c.send_data((uint8_t) info->wrapModeV); - - // MaterialInfoPortalEXT - SEND_STYPE(c, ext->sType); - c.send_data((uint8_t) ext->rayPortalIndex); - SEND_FLOAT(c, ext->rotationSpeed); - } + *out_handle = newHandle; - WAIT_FOR_SERVER_RESPONSE("CreateMaterial()", 0, currentUID); - uint64_t* result = nullptr; - PULL_DATA(sizeof(uint64_t), result); - DeviceBridge::pop_front(); - return *result; - } + return REMIXAPI_ERROR_CODE_SUCCESS; +} - void BRIDGEAPI_CALL bridgeapi_DestroyMaterial(uint64_t handle) { - ClientMessage c(Commands::Api_DestroyMaterial); - SEND_U64(c, handle); +remixapi_ErrorCode REMIXAPI_CALL remixapi_DestroyMaterial(remixapi_MaterialHandle _handle) { + ASSERT_REMIXAPI_PFN_TYPE(remixapi_DestroyMaterial); + HandleUID handle(_handle); + if(!handle.isValid()) { + return REMIXAPI_ERROR_CODE_INVALID_ARGUMENTS; } + { + ClientMessage c(Commands::RemixApi_DestroyMaterial); + send(c, handle); + } + return REMIXAPI_ERROR_CODE_SUCCESS; +} - uint64_t BRIDGEAPI_CALL bridgeapi_CreateTriangleMesh(const x86::remixapi_MeshInfo* info) { - UID currentUID = 0; - { - ClientMessage c(Commands::Api_CreateTriangleMesh); - currentUID = c.get_uid(); - - // MeshInfo - SEND_STYPE(c, info->sType); - SEND_U64(c, info->hash); - - // send each surface - SEND_U32(c, info->surfaces_count); // send surface count before sending the surfaces - for (uint32_t s = 0u; s < info->surfaces_count; s++) - { - const auto& surf = info->surfaces_values[s]; - - // send vertices of the current surface - SEND_U64(c, surf.vertices_count); // send vertex count before vertices - for (uint64_t v = 0u; v < surf.vertices_count; v++) - { - const auto& vert = surf.vertices_values[v]; - SEND_FLOAT(c, vert.position[0]); - SEND_FLOAT(c, vert.position[1]); - SEND_FLOAT(c, vert.position[2]); - SEND_FLOAT(c, vert.normal[0]); - SEND_FLOAT(c, vert.normal[1]); - SEND_FLOAT(c, vert.normal[2]); - SEND_FLOAT(c, vert.texcoord[0]); - SEND_FLOAT(c, vert.texcoord[1]); - SEND_U32(c, vert.color); - } +remixapi_ErrorCode REMIXAPI_CALL remixapi_CreateMesh( + const remixapi_MeshInfo* info, + remixapi_MeshHandle* out_handle) { - // send indices of the current surface - SEND_U64(c, surf.indices_count); // send index count before indices - for (uint64_t i = 0u; i < surf.indices_count; i++) { - SEND_U32(c, surf.indices_values[i]); - } + ASSERT_REMIXAPI_PFN_TYPE(remixapi_CreateMesh); + assert(info->sType == REMIXAPI_STRUCT_TYPE_MESH_INFO); - SEND_U32(c, surf.skinning_hasvalue); - // # TODO skinning + HandleUID newHandle; + { + ClientMessage c(Commands::RemixApi_CreateMesh); + + serializeAndSend(c, *info); - // using remixapi_MaterialHandle is unpractical and kinda unsafe because its only 4 bytes (ptr) - // so user would have to send an actual pointer instead of the uint64_t hash val - SEND_U64(c, surf.material); + const void* infoItr = info; + while (auto* const pNext = getPNext(infoItr)) { + switch (getSType(pNext)) { + default: + { + Logger::warn("[remixapi_CreateMesh] Unknown sType. Skipping."); + break; + } } } - - WAIT_FOR_SERVER_RESPONSE("CreateMesh()", 0, currentUID); - uint64_t* result = nullptr; - PULL_DATA(sizeof(uint64_t), result); - DeviceBridge::pop_front(); - return *result; + send(c, newHandle); } + + *out_handle = newHandle; - void BRIDGEAPI_CALL bridgeapi_DestroyMesh(uint64_t handle) { - ClientMessage c(Commands::Api_DestroyMesh); - SEND_U64(c, handle); - } + return REMIXAPI_ERROR_CODE_SUCCESS; +} - void BRIDGEAPI_CALL bridgeapi_DrawMeshInstance(uint64_t handle, const x86::remixapi_Transform* t, x86::remixapi_Bool double_sided) { - ClientMessage c(Commands::Api_DrawMeshInstance); - SEND_U64(c, handle); - SEND_FLOAT(c, t->matrix[0][0]); SEND_FLOAT(c, t->matrix[0][1]); SEND_FLOAT(c, t->matrix[0][2]); SEND_FLOAT(c, t->matrix[0][3]); - SEND_FLOAT(c, t->matrix[1][0]); SEND_FLOAT(c, t->matrix[1][1]); SEND_FLOAT(c, t->matrix[1][2]); SEND_FLOAT(c, t->matrix[1][3]); - SEND_FLOAT(c, t->matrix[2][0]); SEND_FLOAT(c, t->matrix[2][1]); SEND_FLOAT(c, t->matrix[2][2]); SEND_FLOAT(c, t->matrix[2][3]); - SEND_U32(c, double_sided); +remixapi_ErrorCode REMIXAPI_CALL remixapi_DestroyMesh(remixapi_MeshHandle _handle) { + ASSERT_REMIXAPI_PFN_TYPE(remixapi_DestroyMesh); + HandleUID handle(_handle); + if(!handle.isValid()) { + return REMIXAPI_ERROR_CODE_INVALID_ARGUMENTS; + } + { + ClientMessage c(Commands::RemixApi_DestroyMesh); + send(c, handle); } + return REMIXAPI_ERROR_CODE_SUCCESS; +} - uint64_t BRIDGEAPI_CALL bridgeapi_CreateSphereLight(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoSphereEXT* ext) { - UID currentUID = 0; - { - ClientMessage c(Commands::Api_CreateSphereLight); - currentUID = c.get_uid(); - - // LightInfo - SEND_STYPE(c, info->sType); - SEND_U64(c, info->hash); - SEND_FLOAT3D(c, info->radiance); - - // LightInfoSphereEXT - SEND_STYPE(c, ext->sType); - SEND_FLOAT3D(c, ext->position); - SEND_FLOAT(c, ext->radius); - SEND_U32(c, ext->shaping_hasvalue); - - if (ext->shaping_hasvalue) { - SEND_FLOAT3D(c, ext->shaping_value.direction); - SEND_FLOAT(c, ext->shaping_value.coneAngleDegrees); - SEND_FLOAT(c, ext->shaping_value.coneSoftness); - SEND_FLOAT(c, ext->shaping_value.focusExponent); +remixapi_ErrorCode REMIXAPI_CALL remixapi_DrawInstance(const remixapi_InstanceInfo* info) { + ASSERT_REMIXAPI_PFN_TYPE(remixapi_DrawInstance); + { + ClientMessage c(Commands::RemixApi_DrawInstance); + + serializeAndSend(c, *info); + + // For each valid pNext, we will send a true-valued bool to indicate that + // server must read another extension. If it reads false, it knows that it + // is done reading. + // send(c, Bool::True); -> CONTINUE + // send(c, Bool::False); -> STOP + const void* infoItr = info; + while (auto* const pNext = getPNext(infoItr)) { + infoItr = pNext; + switch (getSType(pNext)) { + case REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_OBJECT_PICKING_EXT: + { + auto* pObjectPicking = static_cast(infoItr); + send(c, Bool::True); + serializeAndSend(c, *pObjectPicking); + break; + } + case REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_BLEND_EXT: + { + auto* pBlend = static_cast(infoItr); + send(c, Bool::True); + serializeAndSend(c, *pBlend); + break; + } + case REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_BONE_TRANSFORMS_EXT: + { + auto* pXforms = static_cast(infoItr); + send(c, Bool::True); + serializeAndSend(c, *pXforms); + break; + } + default: + { + Logger::warn("[remixapi_DrawInstance] Unknown sType. Skipping."); + break; + } } } - - WAIT_FOR_SERVER_RESPONSE("CreateLight()", 0, currentUID); - uint64_t* result = nullptr; - PULL_DATA(sizeof(uint64_t), result); - DeviceBridge::pop_front(); - return *result; + send(c, Bool::False); } + return REMIXAPI_ERROR_CODE_SUCCESS; +} - uint64_t BRIDGEAPI_CALL bridgeapi_CreateRectLight(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoRectEXT* ext) { - UID currentUID = 0; - { - ClientMessage c(Commands::Api_CreateRectLight); - currentUID = c.get_uid(); - - // LightInfo - SEND_STYPE(c, info->sType); - SEND_U64(c, info->hash); - SEND_FLOAT3D(c, info->radiance); - - // LightInfoRectEXT - SEND_STYPE(c, ext->sType); - SEND_FLOAT3D(c, ext->position); - SEND_FLOAT3D(c, ext->xAxis); - SEND_FLOAT(c, ext->xSize); - SEND_FLOAT3D(c, ext->yAxis); - SEND_FLOAT(c, ext->ySize); - SEND_FLOAT3D(c, ext->direction); - SEND_U32(c, ext->shaping_hasvalue); - - if (ext->shaping_hasvalue) { - SEND_FLOAT3D(c, ext->shaping_value.direction); - SEND_FLOAT(c, ext->shaping_value.coneAngleDegrees); - SEND_FLOAT(c, ext->shaping_value.coneSoftness); - SEND_FLOAT(c, ext->shaping_value.focusExponent); +remixapi_ErrorCode REMIXAPI_CALL remixapi_CreateLight( + const remixapi_LightInfo* info, + remixapi_LightHandle* out_handle) { + + ASSERT_REMIXAPI_PFN_TYPE(remixapi_CreateLight); + assert(info->sType == REMIXAPI_STRUCT_TYPE_LIGHT_INFO); + + HandleUID newHandle; + { + ClientMessage c(Commands::RemixApi_CreateLight); + + serializeAndSend(c, *info); + + // For each valid pNext, we will send a true-valued bool to indicate that + // server must read another extension. If it reads false, it knows that it + // is done reading. + // send(c, Bool::True); -> CONTINUE + // send(c, Bool::False); -> STOP + const void* infoItr = info; + while (auto* const pNext = getPNext(infoItr)) { + infoItr = pNext; + switch (getSType(infoItr)) { + case REMIXAPI_STRUCT_TYPE_LIGHT_INFO_SPHERE_EXT: + { + auto* pSphere = static_cast(infoItr); + send(c, Bool::True); + serializeAndSend(c, *pSphere); + break; + } + case REMIXAPI_STRUCT_TYPE_LIGHT_INFO_RECT_EXT: + { + auto* pRect = static_cast(infoItr); + send(c, Bool::True); + serializeAndSend(c, *pRect); + break; + } + case REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DISK_EXT: + { + auto* pDisk = static_cast(infoItr); + send(c, Bool::True); + serializeAndSend(c, *pDisk); + break; + } + case REMIXAPI_STRUCT_TYPE_LIGHT_INFO_CYLINDER_EXT: + { + auto* pCylinder = static_cast(infoItr); + send(c, Bool::True); + serializeAndSend(c, *pCylinder); + break; + } + case REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DISTANT_EXT: + { + auto* pDistant = static_cast(infoItr); + send(c, Bool::True); + serializeAndSend(c, *pDistant); + break; + } + case REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DOME_EXT: + { + auto* pDome = static_cast(infoItr); + send(c, Bool::True); + serializeAndSend(c, *pDome); + break; + } + case REMIXAPI_STRUCT_TYPE_LIGHT_INFO_USD_EXT: + { + auto* pUSD = static_cast(infoItr); + send(c, Bool::True); + serializeAndSend(c, *pUSD); + break; + } + default: + { + Logger::warn("[remixapi_CreateLight] Unknown sType. Skipping."); + break; + } } } - - WAIT_FOR_SERVER_RESPONSE("CreateLight()", 0, currentUID); - uint64_t* result = nullptr; - PULL_DATA(sizeof(uint64_t), result); - DeviceBridge::pop_front(); - return *result; + send(c, Bool::False); + send(c, newHandle); } - uint64_t BRIDGEAPI_CALL bridgeapi_CreateDiscLight(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoDiskEXT* ext) { - UID currentUID = 0; - { - ClientMessage c(Commands::Api_CreateDiskLight); - currentUID = c.get_uid(); - - // LightInfo - SEND_STYPE(c, info->sType); - SEND_U64(c, info->hash); - SEND_FLOAT3D(c, info->radiance); - - // LightInfoDiskEXT - SEND_STYPE(c, ext->sType); - SEND_FLOAT3D(c, ext->position); - SEND_FLOAT3D(c, ext->xAxis); - SEND_FLOAT(c, ext->xRadius); - SEND_FLOAT3D(c, ext->yAxis); - SEND_FLOAT(c, ext->yRadius); - SEND_FLOAT3D(c, ext->direction); - SEND_U32(c, ext->shaping_hasvalue); - - if (ext->shaping_hasvalue) { - SEND_FLOAT3D(c, ext->shaping_value.direction); - SEND_FLOAT(c, ext->shaping_value.coneAngleDegrees); - SEND_FLOAT(c, ext->shaping_value.coneSoftness); - SEND_FLOAT(c, ext->shaping_value.focusExponent); - } - } + *out_handle = newHandle; + + return REMIXAPI_ERROR_CODE_SUCCESS; +} - WAIT_FOR_SERVER_RESPONSE("CreateLight()", 0, currentUID); - uint64_t* result = nullptr; - PULL_DATA(sizeof(uint64_t), result); - DeviceBridge::pop_front(); - return *result; +remixapi_ErrorCode REMIXAPI_CALL remixapi_DestroyLight(remixapi_LightHandle _handle) { + ASSERT_REMIXAPI_PFN_TYPE(remixapi_DestroyLight); + HandleUID handle(_handle); + if(!handle.isValid()) { + return REMIXAPI_ERROR_CODE_INVALID_ARGUMENTS; } + { + ClientMessage c(Commands::RemixApi_DestroyLight); + send(c, handle); + } + return REMIXAPI_ERROR_CODE_SUCCESS; +} - uint64_t BRIDGEAPI_CALL bridgeapi_CreateCylinderLight(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoCylinderEXT* ext) { - UID currentUID = 0; - { - ClientMessage c(Commands::Api_CreateCylinderLight); - currentUID = c.get_uid(); - - // LightInfo - SEND_STYPE(c, info->sType); - SEND_U64(c, info->hash); - SEND_FLOAT3D(c, info->radiance); - - // LightInfoCylinderEXT - SEND_STYPE(c, ext->sType); - SEND_FLOAT3D(c, ext->position); - SEND_FLOAT(c, ext->radius); - SEND_FLOAT3D(c, ext->axis); - SEND_FLOAT(c, ext->axisLength); - } +remixapi_ErrorCode REMIXAPI_CALL remixapi_DrawLightInstance(remixapi_LightHandle _lightHandle) { + ASSERT_REMIXAPI_PFN_TYPE(remixapi_DrawLightInstance); + HandleUID handle(_lightHandle); + if(!handle.isValid()) { + return REMIXAPI_ERROR_CODE_INVALID_ARGUMENTS; + } + { + ClientMessage c(Commands::RemixApi_DrawLightInstance); + send(c, handle); + } + return REMIXAPI_ERROR_CODE_SUCCESS; +} - WAIT_FOR_SERVER_RESPONSE("CreateLight()", 0, currentUID); - uint64_t* result = nullptr; - PULL_DATA(sizeof(uint64_t), result); - DeviceBridge::pop_front(); - return *result; +remixapi_ErrorCode REMIXAPI_CALL remixapi_SetConfigVariable(const char* var, const char* value) { + ASSERT_REMIXAPI_PFN_TYPE(remixapi_SetConfigVariable); + if (!var || !value) { + return REMIXAPI_ERROR_CODE_INVALID_ARGUMENTS; } + { + ClientMessage c(Commands::RemixApi_SetConfigVariable); + send(c, var); + send(c, value); + } + return REMIXAPI_ERROR_CODE_SUCCESS; +} - uint64_t BRIDGEAPI_CALL bridgeapi_CreateDistantLight(const x86::remixapi_LightInfo* info, const x86::remixapi_LightInfoDistantEXT* ext) { - UID currentUID = 0; - { - ClientMessage c(Commands::Api_CreateDistantLight); - currentUID = c.get_uid(); - - // LightInfo - SEND_STYPE(c, info->sType); - SEND_U64(c, info->hash); - SEND_FLOAT3D(c, info->radiance); - - // LightInfoDistantEXT - SEND_STYPE(c, ext->sType); - SEND_FLOAT3D(c, ext->direction); - SEND_FLOAT(c, ext->angularDiameterDegrees); - } +remixapi_ErrorCode REMIXAPI_CALL remixapi_dxvk_CreateD3D9( + remixapi_Bool editorModeEnabled, + IDirect3D9Ex** out_pD3D9) { + ASSERT_REMIXAPI_PFN_TYPE(remixapi_dxvk_CreateD3D9); + Logger::err("[remixapi_dxvk_CreateD3D9] Not yet supported. Device used by Remix API defaults to " + "most recently created by client application."); + *out_pD3D9 = nullptr; + return REMIXAPI_ERROR_CODE_GENERAL_FAILURE; +} - WAIT_FOR_SERVER_RESPONSE("CreateLight()", 0, currentUID); - uint64_t* result = nullptr; - PULL_DATA(sizeof(uint64_t), result); - DeviceBridge::pop_front(); - return *result; +remixapi_ErrorCode REMIXAPI_CALL remixapi_dxvk_RegisterD3D9Device(IDirect3DDevice9Ex* d3d9Device) { + ASSERT_REMIXAPI_PFN_TYPE(remixapi_dxvk_RegisterD3D9Device); + if (!d3d9Device) { + return REMIXAPI_ERROR_CODE_INVALID_ARGUMENTS; } + Logger::err("[remixapi_dxvk_RegisterD3D9Device] Not yet supported. Device used by Remix API defaults to " + "most recently created by client application."); + return REMIXAPI_ERROR_CODE_GENERAL_FAILURE; +} - void BRIDGEAPI_CALL bridgeapi_DestroyLight(uint64_t handle) { - ClientMessage c(Commands::Api_DestroyLight); - SEND_U64(c, handle); - } +// https://stackoverflow.com/a/27490954 +constexpr bool strings_equal(char const * a, char const * b) { + return *a == *b && (*a == '\0' || strings_equal(a + 1, b + 1)); +} - void BRIDGEAPI_CALL bridgeapi_DrawLightInstance(uint64_t handle) { - ClientMessage c(Commands::Api_DrawLightInstance); - SEND_U64(c, handle); - } +extern "C" { + + DLLEXPORT remixapi_ErrorCode __stdcall remixapi_InitializeLibrary( + const remixapi_InitializeLibraryInfo* info, + remixapi_Interface* out_result) { - void BRIDGEAPI_CALL bridgeapi_SetConfigVariable(const char* var, const char* value) { - if (var && value) + static_assert(strings_equal(__func__, remixapi::exported_func_name::initRemixApi)); + ASSERT_REMIXAPI_PFN_TYPE(remixapi_InitializeLibrary); + + if (!GlobalOptions::getExposeRemixApi()) { + Logger::err("Remix API is not enabled. This is currently an experimental feature and must be explicitly enabled \ + in the `bridge.conf`. Please set `exposeRemixApi = True` if you are sure you want it enabled."); + return REMIXAPI_ERROR_CODE_NOT_INITIALIZED; + } + if (!info || info->sType != REMIXAPI_STRUCT_TYPE_INITIALIZE_LIBRARY_INFO) { + return REMIXAPI_ERROR_CODE_INVALID_ARGUMENTS; + } + if (!out_result) { + return REMIXAPI_ERROR_CODE_INVALID_ARGUMENTS; + } + + auto interf = remixapi_Interface {}; { - ClientMessage c(Commands::Api_SetConfigVariable); - c.send_data((uint32_t) strlen(var), var); - c.send_data((uint32_t) strlen(value), value); + // interf.Startup = remixapi_Startup; + // interf.Shutdown = remixapi_Shutdown; + // interf.Present = remixapi_Present; + interf.CreateMaterial = remixapi_CreateMaterial; + interf.DestroyMaterial = remixapi_DestroyMaterial; + interf.CreateMesh = remixapi_CreateMesh; + interf.DestroyMesh = remixapi_DestroyMesh; + // interf.SetupCamera = remixapi_SetupCamera; + interf.DrawInstance = remixapi_DrawInstance; + interf.CreateLight = remixapi_CreateLight; + interf.DestroyLight = remixapi_DestroyLight; + interf.DrawLightInstance = remixapi_DrawLightInstance; + interf.SetConfigVariable = remixapi_SetConfigVariable; + interf.dxvk_CreateD3D9 = remixapi_dxvk_CreateD3D9; + interf.dxvk_RegisterD3D9Device = remixapi_dxvk_RegisterD3D9Device; + // interf.dxvk_GetExternalSwapchain = remixapi_dxvk_GetExternalSwapchain; + // interf.dxvk_GetVkImage = remixapi_dxvk_GetVkImage; + // interf.dxvk_CopyRenderingOutput = remixapi_dxvk_CopyRenderingOutput; + // interf.dxvk_SetDefaultOutput = remixapi_dxvk_SetDefaultOutput; + // interf.pick_RequestObjectPicking = remixapi_pick_RequestObjectPicking; + // interf.pick_HighlightObjects = remixapi_pick_HighlightObjects; } - } - void BRIDGEAPI_CALL bridgeapi_RegisterDevice() { - ClientMessage c(Commands::Api_RegisterDevice); - } + *out_result = interf; + remixapi::g_bInterfaceInitialized = true; - BRIDGE_API void bridgeapi_RegisterEndSceneCallback(PFN_bridgeapi_RegisterEndSceneCallback callback) { - remix_api::interfaceGameCallback = callback; + return REMIXAPI_ERROR_CODE_SUCCESS; + } + + DLLEXPORT remixapi_ErrorCode __stdcall remixapi_RegisterCallbacks( + PFN_remixapi_BridgeCallback beginSceneCallback, + PFN_remixapi_BridgeCallback endSceneCallback, + PFN_remixapi_BridgeCallback presentCallback) { + + static_assert(strings_equal(__func__, remixapi::exported_func_name::registerCallbacks)); + ASSERT_REMIXAPI_PFN_TYPE(remixapi_RegisterCallbacks); + + remixapi::g_beginSceneCallback = beginSceneCallback; + remixapi::g_endSceneCallback = endSceneCallback; + remixapi::g_presentCallback = presentCallback; + return REMIXAPI_ERROR_CODE_SUCCESS; } - extern "C" { - BRIDGE_API BRIDGEAPI_ErrorCode __cdecl bridgeapi_InitFuncs(bridgeapi_Interface* out_result) { - if (!out_result) { - return BRIDGEAPI_ERROR_CODE_INVALID_ARGUMENTS; - } - auto interf = bridgeapi_Interface {}; - { - interf.DebugPrint = bridgeapi_DebugPrint; - interf.CreateOpaqueMaterial = bridgeapi_CreateOpaqueMaterial; - interf.CreateTranslucentMaterial = bridgeapi_CreateTranslucentMaterial; - interf.CreatePortalMaterial = bridgeapi_CreatePortalMaterial; - interf.DestroyMaterial = bridgeapi_DestroyMaterial; - interf.CreateTriangleMesh = bridgeapi_CreateTriangleMesh; - interf.DestroyMesh = bridgeapi_DestroyMesh; - interf.DrawMeshInstance = bridgeapi_DrawMeshInstance; - interf.CreateSphereLight = bridgeapi_CreateSphereLight; - interf.CreateRectLight = bridgeapi_CreateRectLight; - interf.CreateDiskLight = bridgeapi_CreateDiscLight; - interf.CreateCylinderLight = bridgeapi_CreateCylinderLight; - interf.CreateDistantLight = bridgeapi_CreateDistantLight; - interf.DestroyLight = bridgeapi_DestroyLight; - interf.DrawLightInstance = bridgeapi_DrawLightInstance; - interf.SetConfigVariable = bridgeapi_SetConfigVariable; - interf.RegisterDevice = bridgeapi_RegisterDevice; - interf.RegisterEndSceneCallback = bridgeapi_RegisterEndSceneCallback; - } - - *out_result = interf; - remix_api::interfaceInitialized = true; +} - return BRIDGEAPI_ERROR_CODE_SUCCESS; - } - } } diff --git a/src/client/remix_api.h b/src/client/remix_api.h index 61e4cce..7811784 100644 --- a/src/client/remix_api.h +++ b/src/client/remix_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -21,9 +21,13 @@ */ #pragma once -#include "remix_api/bridge_c.h" +#include + +namespace remixapi { + +extern bool g_bInterfaceInitialized; +extern PFN_remixapi_BridgeCallback g_beginSceneCallback; +extern PFN_remixapi_BridgeCallback g_endSceneCallback; +extern PFN_remixapi_BridgeCallback g_presentCallback; -namespace remix_api { - extern bool interfaceInitialized; - extern PFN_bridgeapi_RegisterEndSceneCallback interfaceGameCallback; } diff --git a/src/meson.build b/src/meson.build index 9a9211e..42f4eb4 100644 --- a/src/meson.build +++ b/src/meson.build @@ -22,10 +22,13 @@ subdir('tracy') subdir('util') -if cpu_family == 'x86' - subdir('client') - subdir('server') - subdir('launcher') -elif cpu_family == 'x86_64' - subdir('server') + +if not enable_tests + if cpu_family == 'x86' + subdir('client') + subdir('server') + subdir('launcher') + elif cpu_family == 'x86_64' + subdir('server') + endif endif diff --git a/src/server/main.cpp b/src/server/main.cpp index 6326aa8..240694e 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -23,6 +23,7 @@ #include "version.h" #include "module_processing.h" +#include "remix_api.h" #include "util_bridge_assert.h" #include "util_circularbuffer.h" @@ -34,6 +35,7 @@ #include "util_hack_d3d_debug.h" #include "util_messagechannel.h" #include "util_modulecommand.h" +#include "util_remixapi.h" #include "util_seh.h" #include "util_semaphore.h" #include "util_sharedheap.h" @@ -45,6 +47,7 @@ #include "config/config.h" #include "config/global_options.h" #include "server_options.h" +#include "../client/client_options.h" #include "../tracy/tracy.hpp" #include @@ -54,11 +57,9 @@ #include #include -#include "remix_api/remix_c.h" -#include "../client/client_options.h" - using namespace Commands; using namespace bridge_util; +using namespace remixapi::util; // NOTE: This extension is really useful for debugging the Bridge child process from the parent process: // https://marketplace.visualstudio.com/items?itemName=vsdbgplat.MicrosoftChildProcessDebuggingPowerTool @@ -102,37 +103,6 @@ using namespace bridge_util; const auto& name = map[name##Handle]; \ assert(name != NULL) -namespace { - remixapi_StructType pullSType() { - return (remixapi_StructType) DeviceBridge::get_data(); - } - - int32_t pullInt() { - return (int32_t) DeviceBridge::get_data(); - } - - uint32_t pullUInt32() { - return (uint32_t) DeviceBridge::get_data(); - } - - uint64_t pullUInt64() { - uint64_t* r = nullptr; - uint32_t s = DeviceBridge::get_data((void**) &r); - assert(s == 0 || sizeof(uint64_t) == s); - return *r; - } - - std::wstring pullPath() { - wchar_t* t = nullptr; - const uint32_t len = DeviceBridge::getReaderChannel().data->pull((void**) &t) / sizeof(wchar_t); - return std::wstring(t, len); - } - - float pullFloat() { - return *(float*)&DeviceBridge::get_data(); - } -} - // NOTE: MSDN states HWNDs are safe to cross x86-->x64 boundary, and that a truncating cast should be used: // https://docs.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication?redirectedfrom=MSDN #define TRUNCATE_HANDLE(type, input) (type)(size_t)(input) @@ -162,6 +132,7 @@ std::unordered_map gpD3DVertexShaders; std::unordered_map gpD3DPixelShaders; std::unordered_map gpD3DSwapChains; std::unordered_map gpD3DQuery; +std::unordered_map gMapRemixApi; std::mutex gLock; @@ -169,6 +140,19 @@ std::mutex gLock; bool gbBridgeRunning = true; HANDLE hWait; +namespace { +template +static void deserializeFromQueue(SerializableT& serializableT) { + static_assert(is_serializable_v, "deserializeRemixApiT(...) may only be called with defined Serializable types"); + void* pSlzdData = nullptr; + const auto size = DeviceBridge::get_data(&pSlzdData); + SerializableT dslz(pSlzdData); + assert(size == dslz.size()); + dslz.deserialize(); + serializableT = std::move(dslz); +} +} + static inline void safeDestroy(IUnknown* obj, uint32_t x86handle) { // Note: in DXVK the refcounts of non-standalone objects may go negative! // We need to handle such objects appropriately, even though this is not @@ -2713,487 +2697,360 @@ void ProcessDeviceCommandQueue() { /* * BridgeApi commands */ - case Api_DebugPrint: - { - void* text_ptr = nullptr; - const uint32_t text_size = DeviceBridge::getReaderChannel().data->pull(&text_ptr); - Logger::info(std::string((const char*) text_ptr, text_size)); - break; - } - - case Api_CreateOpaqueMaterial: - { - std::wstring albedo {}, normal {}, tangent {}, emissive {}, rough {}, metal {}, height {}, sstrans {}, ssthick {}, ssscatter {}; - remixapi_MaterialInfo info = {}; - { - info.sType = pullSType(); - info.hash = pullUInt64(); - albedo = pullPath(); info.albedoTexture = albedo.c_str(); - normal = pullPath(); info.normalTexture = normal.c_str(); - tangent = pullPath(); info.tangentTexture = tangent.c_str(); - emissive = pullPath(); info.emissiveTexture = emissive.c_str(); - info.emissiveIntensity = pullFloat(); - info.emissiveColorConstant = { pullFloat(), pullFloat(), pullFloat() }; - info.spriteSheetRow = (uint8_t) DeviceBridge::get_data(); - info.spriteSheetCol = (uint8_t) DeviceBridge::get_data(); - info.spriteSheetFps = (uint8_t) DeviceBridge::get_data(); - info.filterMode = (uint8_t) DeviceBridge::get_data(); - info.wrapModeU = (uint8_t) DeviceBridge::get_data(); - info.wrapModeV = (uint8_t) DeviceBridge::get_data(); - } - - remixapi_MaterialInfoOpaqueEXT ext = {}; - { - ext.sType = pullSType(); - rough = pullPath(); ext.roughnessTexture = rough.c_str(); - metal = pullPath(); ext.metallicTexture = metal.c_str(); - ext.anisotropy = pullFloat(); - ext.albedoConstant = { pullFloat(), pullFloat(), pullFloat() }; - ext.opacityConstant = pullFloat(); - ext.roughnessConstant = pullFloat(); - ext.metallicConstant = pullFloat(); - ext.thinFilmThickness_hasvalue = pullUInt32(); - ext.thinFilmThickness_value = pullFloat(); - ext.alphaIsThinFilmThickness = pullUInt32(); - height = pullPath(); ext.heightTexture = height.c_str(); - ext.heightTextureStrength = pullFloat(); - ext.useDrawCallAlphaState = pullUInt32(); // If true, InstanceInfoBlendEXT is used as a source for alpha state - ext.blendType_hasvalue = pullUInt32(); - ext.blendType_value = pullInt(); - ext.invertedBlend = pullUInt32(); - ext.alphaTestType = pullInt(); - ext.alphaReferenceValue = (uint8_t) DeviceBridge::get_data(); - } - - remixapi_MaterialInfoOpaqueSubsurfaceEXT ext_ss = {}; - const remixapi_Bool has_subsurface = pullUInt32(); - if (has_subsurface) { - ext_ss.sType = pullSType(); - sstrans = pullPath(); ext_ss.subsurfaceTransmittanceTexture = sstrans.c_str(); - ssthick = pullPath(); ext_ss.subsurfaceThicknessTexture = ssthick.c_str(); - ssscatter = pullPath(); ext_ss.subsurfaceSingleScatteringAlbedoTexture = ssscatter.c_str(); - ext_ss.subsurfaceTransmittanceColor = { pullFloat(), pullFloat(), pullFloat() }; - ext_ss.subsurfaceMeasurementDistance = pullFloat(); - ext_ss.subsurfaceSingleScatteringAlbedo = { pullFloat(), pullFloat(), pullFloat() }; - ext_ss.subsurfaceVolumetricAnisotropy = pullFloat(); - - // MaterialInfo -> OpaqueSubsurfaceEXT -> OpaqueEXT - ext_ss.pNext = &ext; - info.pNext = &ext_ss; - } else { - info.pNext = &ext; // MaterialInfo -> OpaqueEXT - } - - remixapi_MaterialHandle temp_handle = nullptr; - remix_api::g_remix.CreateMaterial(&info, &temp_handle); + case RemixApi_CreateMaterial: + { + // Rather than allocate deserialized struct extensions on the heap, + // allocate them locally, since we know only one instance will be + // supported at a time + struct MaterialExtensions { + serialize::MaterialInfoOpaque opaque; + serialize::MaterialInfoOpaqueSubsurface opaqueSubsurface; + serialize::MaterialInfoTranslucent translucent; + serialize::MaterialInfoPortal portal; + } exts; + memset(&exts, 0, sizeof(MaterialExtensions)); + + const auto matInfoSType = remixapi::pullSType(); + assert(matInfoSType == REMIXAPI_STRUCT_TYPE_MATERIAL_INFO); + serialize::MaterialInfo matInfo; + deserializeFromQueue(matInfo); - ServerMessage c(Commands::Bridge_Response, currentUID); - c.send_data(sizeof(uint64_t), &temp_handle); - break; - } - - case Api_CreateTranslucentMaterial: - { - std::wstring albedo {}, normal {}, tangent {}, emissive {}, transmittance {}; - remixapi_MaterialInfo info = {}; - { - info.sType = pullSType(); - info.hash = pullUInt64(); - albedo = pullPath(); info.albedoTexture = albedo.c_str(); - normal = pullPath(); info.normalTexture = normal.c_str(); - tangent = pullPath(); info.tangentTexture = tangent.c_str(); - emissive = pullPath(); info.emissiveTexture = emissive.c_str(); - - info.emissiveIntensity = pullFloat(); - info.emissiveColorConstant = { pullFloat(), pullFloat(), pullFloat() }; - info.spriteSheetRow = (uint8_t) DeviceBridge::get_data(); - info.spriteSheetCol = (uint8_t) DeviceBridge::get_data(); - info.spriteSheetFps = (uint8_t) DeviceBridge::get_data(); - info.filterMode = (uint8_t) DeviceBridge::get_data(); - info.wrapModeU = (uint8_t) DeviceBridge::get_data(); - info.wrapModeV = (uint8_t) DeviceBridge::get_data(); - } - - remixapi_MaterialInfoTranslucentEXT ext = {}; - { - ext.sType = pullSType(); - transmittance = pullPath(); ext.transmittanceTexture = transmittance.c_str(); - ext.refractiveIndex = pullFloat(); - ext.transmittanceColor = { pullFloat(), pullFloat(), pullFloat() }; - ext.transmittanceMeasurementDistance = pullFloat(); - ext.thinWallThickness_hasvalue = pullUInt32(); - ext.thinWallThickness_value = pullFloat(); - ext.useDiffuseLayer = pullUInt32(); + matInfo.pNext = nullptr; + + bool bMatExtExists = remixapi::pullBool(); + auto* pInfoProto = &getInfoProto(matInfo); + while(bMatExtExists) { + const auto extSType = remixapi::pullSType(); + switch (extSType) { + case REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_OPAQUE_EXT: + { + assert(!exts.opaque.pNext); + deserializeFromQueue(exts.opaque); + pInfoProto->pNext = &(exts.opaque); + pInfoProto = &getInfoProto(exts.opaque); + break; + } + case REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_OPAQUE_SUBSURFACE_EXT: + { + assert(!exts.opaqueSubsurface.pNext); + deserializeFromQueue(exts.opaqueSubsurface); + pInfoProto->pNext = &(exts.opaqueSubsurface); + pInfoProto = &getInfoProto(exts.opaqueSubsurface); + break; + } + case REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_TRANSLUCENT_EXT: + { + assert(!exts.translucent.pNext); + deserializeFromQueue(exts.translucent); + pInfoProto->pNext = &(exts.translucent); + pInfoProto = &getInfoProto(exts.translucent); + break; + } + case REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_PORTAL_EXT: + { + assert(!exts.portal.pNext); + deserializeFromQueue(exts.portal); + pInfoProto->pNext = &(exts.portal); + pInfoProto = &getInfoProto(exts.portal); + break; + } + default: + { + Logger::warn("[Api_CreateMaterial] Unknown sType. Skipping."); + break; + } + } + bMatExtExists = remixapi::pullBool(); } - // assign ext - info.pNext = &ext; - - remixapi_MaterialHandle temp_handle = nullptr; - remix_api::g_remix.CreateMaterial(&info, &temp_handle); - - ServerMessage c(Commands::Bridge_Response, currentUID); - c.send_data(sizeof(uint64_t), &temp_handle); - break; - } + HandleUID handle = remixapi::pullHandle(); + assert(handle.isValid()); - case Api_CreatePortalMaterial: - { - std::wstring albedo {}, normal {}, tangent {}, emissive {}; - remixapi_MaterialInfo info = {}; - { - info.sType = pullSType(); - info.hash = pullUInt64(); - albedo = pullPath(); info.albedoTexture = albedo.c_str(); - normal = pullPath(); info.normalTexture = normal.c_str(); - tangent = pullPath(); info.tangentTexture = tangent.c_str(); - emissive = pullPath(); info.emissiveTexture = emissive.c_str(); - - info.emissiveIntensity = pullFloat(); - info.emissiveColorConstant = { pullFloat(), pullFloat(), pullFloat() }; - info.spriteSheetRow = (uint8_t) DeviceBridge::get_data(); - info.spriteSheetCol = (uint8_t) DeviceBridge::get_data(); - info.spriteSheetFps = (uint8_t) DeviceBridge::get_data(); - info.filterMode = (uint8_t) DeviceBridge::get_data(); - info.wrapModeU = (uint8_t) DeviceBridge::get_data(); - info.wrapModeV = (uint8_t) DeviceBridge::get_data(); - } - - remixapi_MaterialInfoPortalEXT ext = {}; - { - ext.sType = pullSType(); - ext.rayPortalIndex = (uint8_t) DeviceBridge::get_data(); - ext.rotationSpeed = pullFloat(); + remixapi_MaterialHandle matHandle = nullptr; + if(remixapi::g_remix.CreateMaterial(&matInfo, &matHandle) == REMIXAPI_ERROR_CODE_SUCCESS) { + gMapRemixApi.emplace(handle, matHandle); + } else { + Logger::err("[RemixApi_CreateMaterial] Remix API call failed!"); } - - // assign ext - info.pNext = &ext; - - remixapi_MaterialHandle temp_handle = nullptr; - remix_api::g_remix.CreateMaterial(&info, &temp_handle); - ServerMessage c(Commands::Bridge_Response, currentUID); - c.send_data(sizeof(uint64_t), &temp_handle); break; } - case Api_DestroyMaterial: + case RemixApi_DestroyMaterial: { - uint64_t material_handle = pullUInt64(); - - if (material_handle) { - remix_api::g_remix.DestroyMaterial((remixapi_MaterialHandle) material_handle); + HandleUID handle = remixapi::pullHandle(); + const bool bHandleIsValid = + handle.isValid() && gMapRemixApi.find(handle) != gMapRemixApi.cend(); + assert(bHandleIsValid); + if(bHandleIsValid) { + remixapi::g_remix.DestroyMaterial(handle); + gMapRemixApi.erase(handle); } else { - Logger::debug("[RemixApi] DestroyMaterial(): Invalid material handle"); + Logger::err("[RemixApi_DestroyMaterial] Invalid material handle!" ); } break; } - case Api_CreateTriangleMesh: + case RemixApi_CreateMesh: { - remixapi_MeshInfo info = {}; - { - info.sType = pullSType(); - info.hash = pullUInt64(); - info.surfaces_count = pullUInt32(); // surface count before surfaces - } - - std::vector surfs; - surfs.reserve(8); - - std::vector> verts; - std::vector> indices; - - for (uint32_t s = 0u; s < info.surfaces_count; s++) { - // pull all vertices - verts.emplace_back(); // add new vector entry for current surface - - uint64_t vertex_count = pullUInt64(); - for (uint64_t v = 0u; v < vertex_count; v++) { - verts.back().emplace_back(remixapi_HardcodedVertex - { - { pullFloat(), pullFloat(), pullFloat() }, // position - { pullFloat(), pullFloat(), pullFloat() }, // normal - { pullFloat(), pullFloat() }, // texcoord - pullUInt32() // color - }); - } - - // pull all indices - indices.emplace_back(); // add new vector entry for current surface - - uint64_t index_count = pullUInt64(); - for (uint64_t i = 0u; i < index_count; i++) { - indices.back().emplace_back(pullUInt32()); - } - - uint32_t skinning_hasvalue = pullUInt32(); - uint64_t material_handle = pullUInt64(); - - // build the surface struct - surfs.emplace_back(remixapi_MeshInfoSurfaceTriangles { - verts.back().data(), - vertex_count, - indices.back().data(), - index_count, - skinning_hasvalue, - remixapi_MeshInfoSkinning {}, - (remixapi_MaterialHandle) material_handle - }); + const auto meshInfoSType = remixapi::pullSType(); + assert(meshInfoSType == REMIXAPI_STRUCT_TYPE_MESH_INFO); + serialize::MeshInfo meshInfo; + deserializeFromQueue(meshInfo); + meshInfo.pNext = nullptr; + + for(size_t iSurf = 0; iSurf < meshInfo.surfaces_count; ++iSurf) { + // If we don't const_cast, we'd have to copy the entire + // remixapi_MeshInfo::surfaces_values array in order to get around + // the remixapi_MeshInfo's member const qualifier and reassign + // remixapi_MeshInfoSurfaceTriangles::material. + auto& surf = const_cast(meshInfo.surfaces_values[iSurf]); + HandleUID handle = surf.material; + assert(handle.isValid()); + assert(gMapRemixApi.find(handle) != gMapRemixApi.cend()); + surf.material = (remixapi_MaterialHandle)gMapRemixApi[handle]; + } + + HandleUID handle = remixapi::pullHandle(); + assert(handle.isValid()); + + remixapi_MeshHandle meshHandle = nullptr; + if(remixapi::g_remix.CreateMesh(&meshInfo, &meshHandle) == REMIXAPI_ERROR_CODE_SUCCESS) { + gMapRemixApi.emplace(handle, meshHandle); + } else { + Logger::err("[RemixApi_CreateMesh] Remix API call failed!"); } - // remixapi_MeshInfo - info.surfaces_values = surfs.data(); - - remixapi_MeshHandle temp_handle = nullptr; - remix_api::g_remix.CreateMesh(&info, &temp_handle); - - ServerMessage c(Commands::Bridge_Response, currentUID); - c.send_data(sizeof(uint64_t), &temp_handle); break; } - case Api_DestroyMesh: + case RemixApi_DestroyMesh: { - uint64_t mesh_handle = pullUInt64(); - - if (mesh_handle) { - remix_api::g_remix.DestroyMesh((remixapi_MeshHandle) mesh_handle); + HandleUID handle = remixapi::pullHandle(); + const bool bHandleIsValid = + handle.isValid() && gMapRemixApi.find(handle) != gMapRemixApi.cend(); + assert(bHandleIsValid); + if(bHandleIsValid) { + remixapi::g_remix.DestroyMesh(handle); + gMapRemixApi.erase(handle); } else { - Logger::debug("[RemixApi] DestroyMesh(): Invalid mesh handle"); + Logger::err("[RemixApi_DestroyMesh] Invalid mesh handle!" ); } break; } - case Api_DrawMeshInstance: + case RemixApi_DrawInstance: { - uint64_t mesh_handle = pullUInt64(); + // Rather than allocate deserialized struct extensions on the heap, + // allocate them locally, since we know only one instance will be + // supported at a time + struct InstanceExtensions { + serialize::InstanceInfoObjectPicking objectPicking; + serialize::InstanceInfoBlend blend; + serialize::InstanceInfoTransforms boneXforms; + } exts; + memset(&exts, 0, sizeof(InstanceExtensions)); - remixapi_InstanceInfo inst = {}; - { - inst.sType = REMIXAPI_STRUCT_TYPE_INSTANCE_INFO; - inst.categoryFlags = 0; - inst.mesh = (remixapi_MeshHandle) mesh_handle; - inst.transform = {{ - { pullFloat(), pullFloat(), pullFloat(), pullFloat() }, - { pullFloat(), pullFloat(), pullFloat(), pullFloat() }, - { pullFloat(), pullFloat(), pullFloat(), pullFloat() } - }}; - inst.doubleSided = pullUInt32(); - } - - if (mesh_handle) { - remix_api::g_remix.DrawInstance(&inst); + const auto instSType = remixapi::pullSType(); + assert(instSType == REMIXAPI_STRUCT_TYPE_INSTANCE_INFO); + serialize::InstanceInfo instInfo; + deserializeFromQueue(instInfo); + + HandleUID handle = instInfo.mesh; + const bool bHandleIsValid = + handle.isValid() && gMapRemixApi.find(handle) != gMapRemixApi.cend(); + assert(bHandleIsValid); + if(bHandleIsValid) { + instInfo.mesh = (remixapi_MeshHandle)gMapRemixApi[handle]; } else { - Logger::debug("[RemixApi] DrawInstance(): Invalid mesh handle"); - } - break; - } - - case Api_CreateSphereLight: - { - remixapi_LightInfo l = {}; - { - l.sType = pullSType(); - l.hash = pullUInt64(); - l.radiance = { pullFloat(), pullFloat(), pullFloat() }; - } - - remixapi_LightInfoSphereEXT ext = {}; - { - ext.sType = pullSType(); - ext.pNext = nullptr; - ext.position = { pullFloat(), pullFloat(), pullFloat() }; - ext.radius = pullFloat(); - ext.shaping_hasvalue = pullUInt32(); - - if (ext.shaping_hasvalue) { - ext.shaping_value.direction = { pullFloat(), pullFloat(), pullFloat() }; - ext.shaping_value.coneAngleDegrees = pullFloat(); - ext.shaping_value.coneSoftness = pullFloat(); - ext.shaping_value.focusExponent = pullFloat(); - } + Logger::err("[RemixApi_DrawInstance] Invalid mesh handle!" ); + break; } - // remixapi_LightInfo - l.pNext = &ext; - - remixapi_LightHandle temp_handle = nullptr; - remix_api::g_remix.CreateLight(&l, &temp_handle); + instInfo.pNext = nullptr; - ServerMessage c(Commands::Bridge_Response, currentUID); - c.send_data(sizeof(uint64_t), &temp_handle); - break; - } - - case Api_CreateRectLight: - { - remixapi_LightInfo l = {}; - { - l.sType = pullSType(); - l.hash = pullUInt64(); - l.radiance = { pullFloat(), pullFloat(), pullFloat() }; - } - - remixapi_LightInfoRectEXT ext = {}; - { - ext.sType = pullSType(); - ext.pNext = nullptr; - ext.position = { pullFloat(), pullFloat(), pullFloat() }; - ext.xAxis = { pullFloat(), pullFloat(), pullFloat() }; - ext.xSize = pullFloat(); - ext.yAxis = { pullFloat(), pullFloat(), pullFloat() }; - ext.ySize = pullFloat(); - ext.direction = { pullFloat(), pullFloat(), pullFloat() }; - ext.shaping_hasvalue = pullUInt32(); - - if (ext.shaping_hasvalue) { - ext.shaping_value.direction = { pullFloat(), pullFloat(), pullFloat() }; - ext.shaping_value.coneAngleDegrees = pullFloat(); - ext.shaping_value.coneSoftness = pullFloat(); - ext.shaping_value.focusExponent = pullFloat(); + bool bInstExtExists = remixapi::pullBool(); + auto* pInfoProto = &getInfoProto(instInfo); + while(bInstExtExists) { + const auto extSType = remixapi::pullSType(); + switch (extSType) { + case REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_OBJECT_PICKING_EXT: + { + assert(!exts.objectPicking.pNext); + deserializeFromQueue(exts.objectPicking); + pInfoProto->pNext = &(exts.objectPicking); + pInfoProto = &getInfoProto(exts.objectPicking); + break; + } + case REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_BLEND_EXT: + { + assert(!exts.blend.pNext); + deserializeFromQueue(exts.blend); + pInfoProto->pNext = &(exts.blend); + pInfoProto = &getInfoProto(exts.blend); + break; + } + case REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_BONE_TRANSFORMS_EXT: + { + assert(!exts.boneXforms.pNext); + deserializeFromQueue(exts.boneXforms); + pInfoProto->pNext = &(exts.boneXforms); + pInfoProto = &getInfoProto(exts.boneXforms); + break; + } + default: + { + Logger::warn("[RemixApi_DrawInstance] Unknown sType. Skipping."); + break; + } } + bInstExtExists = remixapi::pullBool(); } - // remixapi_LightInfo - l.pNext = &ext; - - remixapi_LightHandle temp_handle = nullptr; - remix_api::g_remix.CreateLight(&l, &temp_handle); - - ServerMessage c(Commands::Bridge_Response, currentUID); - c.send_data(sizeof(uint64_t), &temp_handle); - break; - } - - case Api_CreateDiskLight: - { - remixapi_LightInfo l = {}; - { - l.sType = pullSType(); - l.hash = pullUInt64(); - l.radiance = { pullFloat(), pullFloat(), pullFloat() }; + if(remixapi::g_remix.DrawInstance(&instInfo) != REMIXAPI_ERROR_CODE_SUCCESS) { + Logger::err("[RemixApi_DrawInstance] Remix API call failed!"); } - remixapi_LightInfoDiskEXT ext = {}; - { - ext.sType = pullSType(); - ext.pNext = nullptr; - ext.position = { pullFloat(), pullFloat(), pullFloat() }; - ext.xAxis = { pullFloat(), pullFloat(), pullFloat() }; - ext.xRadius = pullFloat(); - ext.yAxis = { pullFloat(), pullFloat(), pullFloat() }; - ext.yRadius = pullFloat(); - ext.direction = { pullFloat(), pullFloat(), pullFloat() }; - ext.shaping_hasvalue = pullUInt32(); - - if (ext.shaping_hasvalue) { - ext.shaping_value.direction = { pullFloat(), pullFloat(), pullFloat() }; - ext.shaping_value.coneAngleDegrees = pullFloat(); - ext.shaping_value.coneSoftness = pullFloat(); - ext.shaping_value.focusExponent = pullFloat(); - } - } - - // remixapi_LightInfo - l.pNext = &ext; - - remixapi_LightHandle temp_handle = nullptr; - remix_api::g_remix.CreateLight(&l, &temp_handle); - - ServerMessage c(Commands::Bridge_Response, currentUID); - c.send_data(sizeof(uint64_t), &temp_handle); break; } - case Api_CreateCylinderLight: + case RemixApi_CreateLight: { - remixapi_LightInfo l = {}; - { - l.sType = pullSType(); - l.hash = pullUInt64(); - l.radiance = { pullFloat(), pullFloat(), pullFloat() }; - } - - remixapi_LightInfoCylinderEXT ext = {}; - { - ext.sType = pullSType(); - ext.pNext = nullptr; - ext.position = { pullFloat(), pullFloat(), pullFloat() }; - ext.radius = pullFloat(); - ext.axis = { pullFloat(), pullFloat(), pullFloat() }; - ext.axisLength = pullFloat(); - } - - // remixapi_LightInfo - l.pNext = &ext; - - remixapi_LightHandle temp_handle = nullptr; - remix_api::g_remix.CreateLight(&l, &temp_handle); - - ServerMessage c(Commands::Bridge_Response, currentUID); - c.send_data(sizeof(uint64_t), &temp_handle); - break; - } + // Rather than allocate deserialized struct extensions on the heap, + // allocate them locally, since we know only one instance will be + // supported at a time + struct LightExtensions { + serialize::LightInfoSphere sphere; + serialize::LightInfoRect rect; + serialize::LightInfoDisk disk; + serialize::LightInfoCylinder cylinder; + serialize::LightInfoDistant distant; + serialize::LightInfoDome dome; + serialize::LightInfoUSD usd; + } exts; + memset(&exts, 0, sizeof(LightExtensions)); - case Api_CreateDistantLight: - { - remixapi_LightInfo l = {}; - { - l.sType = pullSType(); - l.hash = pullUInt64(); - l.radiance = { pullFloat(), pullFloat(), pullFloat() }; - } + const auto lightSType = remixapi::pullSType(); + assert(lightSType == REMIXAPI_STRUCT_TYPE_LIGHT_INFO); + serialize::LightInfo lightInfo; + deserializeFromQueue(lightInfo); + lightInfo.pNext = nullptr; - remixapi_LightInfoDistantEXT ext = {}; - { - ext.sType = pullSType(); - ext.pNext = nullptr; - ext.direction = { pullFloat(), pullFloat(), pullFloat() }; - ext.angularDiameterDegrees = pullFloat(); + bool bLightExtExists = remixapi::pullBool(); + auto* pInfoProto = &getInfoProto(lightInfo); + while(bLightExtExists) { + const auto extSType = remixapi::pullSType(); + switch (extSType) { + case REMIXAPI_STRUCT_TYPE_LIGHT_INFO_SPHERE_EXT: + { + assert(!exts.sphere.pNext); + deserializeFromQueue(exts.sphere); + pInfoProto->pNext = &(exts.sphere); + pInfoProto = &getInfoProto(exts.sphere); + break; + } + case REMIXAPI_STRUCT_TYPE_LIGHT_INFO_RECT_EXT: + { + assert(!exts.rect.pNext); + deserializeFromQueue(exts.rect); + pInfoProto->pNext = &(exts.rect); + pInfoProto = &getInfoProto(exts.rect); + break; + } + case REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DISK_EXT: + { + assert(!exts.disk.pNext); + deserializeFromQueue(exts.disk); + pInfoProto->pNext = &(exts.disk); + pInfoProto = &getInfoProto(exts.disk); + break; + } + case REMIXAPI_STRUCT_TYPE_LIGHT_INFO_CYLINDER_EXT: + { + assert(!exts.cylinder.pNext); + deserializeFromQueue(exts.cylinder); + pInfoProto->pNext = &(exts.cylinder); + pInfoProto = &getInfoProto(exts.cylinder); + break; + } + case REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DISTANT_EXT: + { + assert(!exts.distant.pNext); + deserializeFromQueue(exts.distant); + pInfoProto->pNext = &(exts.distant); + pInfoProto = &getInfoProto(exts.distant); + break; + } + case REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DOME_EXT: + { + assert(!exts.dome.pNext); + deserializeFromQueue(exts.dome); + pInfoProto->pNext = &(exts.dome); + pInfoProto = &getInfoProto(exts.dome); + break; + } + case REMIXAPI_STRUCT_TYPE_LIGHT_INFO_USD_EXT: + { + assert(!exts.usd.pNext); + deserializeFromQueue(exts.usd); + pInfoProto->pNext = &(exts.usd); + pInfoProto = &getInfoProto(exts.usd); + break; + } + default: + { + Logger::warn("[RemixApi_CreateLight] Unknown sType. Skipping."); + break; + } + } + bLightExtExists = remixapi::pullBool(); } - // remixapi_LightInfo - l.pNext = &ext; - - remixapi_LightHandle temp_handle = nullptr; - remix_api::g_remix.CreateLight(&l, &temp_handle); + HandleUID handle = remixapi::pullHandle(); + assert(handle.isValid()); - ServerMessage c(Commands::Bridge_Response, currentUID); - c.send_data(sizeof(uint64_t), &temp_handle); + remixapi_LightHandle lightHandle = nullptr; + if(remixapi::g_remix.CreateLight(&lightInfo, &lightHandle) == REMIXAPI_ERROR_CODE_SUCCESS) { + gMapRemixApi.emplace(handle, lightHandle); + } else { + Logger::err("[RemixApi_CreateLight] Remix API call failed!"); + } + break; } - case Api_DestroyLight: + case RemixApi_DestroyLight: { - uint64_t light_handle = pullUInt64(); - - if (light_handle) { - remix_api::g_remix.DestroyLight((remixapi_LightHandle) light_handle); + HandleUID handle = remixapi::pullHandle(); + const bool bHandleIsValid = + handle.isValid() && gMapRemixApi.find(handle) != gMapRemixApi.cend(); + assert(bHandleIsValid); + if(bHandleIsValid) { + remixapi::g_remix.DestroyLight(handle); + gMapRemixApi.erase(handle); } else { - Logger::debug("[RemixApi] DestroyLight(): invalid light handle"); + Logger::err("[RemixApi_DestroyLight] Invalid light handle!" ); } break; } - case Api_DrawLightInstance: + case RemixApi_DrawLightInstance: { - uint64_t light_handle = pullUInt64(); - - if (light_handle) { - remix_api::g_remix.DrawLightInstance((remixapi_LightHandle) light_handle); + HandleUID handle = remixapi::pullHandle(); + const bool bHandleIsValid = + handle.isValid() && gMapRemixApi.find(handle) != gMapRemixApi.cend(); + assert(bHandleIsValid); + if(bHandleIsValid) { + auto lightHandle = (remixapi_LightHandle)gMapRemixApi[handle]; + remixapi::g_remix.DrawLightInstance(lightHandle); } else { - Logger::debug("[RemixApi] DrawLightInstance(): invalid light handle"); + Logger::err("[RemixApi_DrawLightInstance] Invalid light handle!" ); } break; } - case Api_SetConfigVariable: + case RemixApi_SetConfigVariable: { - // the returned size of the string is correct but the const char* - // might not be null terminated correctly so its possible that it - // contains junk data at the end due to the 4 byte sized rpc chuncks? - void* var_ptr = nullptr; const uint32_t var_size = DeviceBridge::getReaderChannel().data->pull(&var_ptr); std::string var_str((const char*) var_ptr, var_size); @@ -3202,22 +3059,24 @@ void ProcessDeviceCommandQueue() { const uint32_t value_size = DeviceBridge::getReaderChannel().data->pull(&value_ptr); std::string value_str((const char*) value_ptr, value_size); - remix_api::g_remix.SetConfigVariable(var_str.c_str(), value_str.c_str()); + remixapi::g_remix.SetConfigVariable(var_str.c_str(), value_str.c_str()); break; } - case Api_RegisterDevice: + case RemixApi_CreateD3D9: { - if (remix_api::g_remix_initialized) { - if (const auto dev = remix_api::getDevice(); dev) { - remixapi_ErrorCode r = remix_api::g_remix.dxvk_RegisterD3D9Device(dev); - Logger::info("[RemixApi] dxvk_RegisterD3D9Device(): " + (!r ? "success" : "error: " + std::to_string(r))); - } else { - Logger::warn("[RemixApi] Failed to get d3d9 device!"); - } - } + Logger::err("[RemixApi_CreateD3D9] Not yet supported. Device used by Remix API defaults to " + "most recently created by client application."); break; } + + case RemixApi_RegisterDevice: + { + Logger::err("[RemixApi_RegisterDevice] Not yet supported. Device used by Remix API defaults to " + "most recently created by client application."); + break; + } + default: break; } @@ -3328,12 +3187,12 @@ bool InitializeD3D() { Logger::info("D3D9 interface object creation succeeded!"); } // Initialize remixApi - if (ClientOptions::getExposeRemixApi()) { - remixapi_ErrorCode status = remixapi_lib_loadRemixDllAndInitialize(L"d3d9.dll", &remix_api::g_remix, &remix_api::g_remix_dll); + if (GlobalOptions::getExposeRemixApi()) { + remixapi_ErrorCode status = remixapi_lib_loadRemixDllAndInitialize(L"d3d9.dll", &remixapi::g_remix, &remixapi::g_remix_dll); if (status != REMIXAPI_ERROR_CODE_SUCCESS) { Logger::err(format_string("[RemixApi] RemixApi initialization failed: %d\n", status)); } else { - remix_api::g_remix_initialized = true; + remixapi::g_remix_initialized = true; Logger::info("[RemixApi] Initialized RemixApi."); } } diff --git a/src/server/meson.build b/src/server/meson.build index 015a3ab..ce88699 100644 --- a/src/server/meson.build +++ b/src/server/meson.build @@ -22,12 +22,14 @@ server_src = files([ 'main.cpp', - 'module_processing.cpp' + 'module_processing.cpp', + 'remix_api.cpp' ]) server_header = files([ 'module_processing.h', - 'server_options.h' + 'server_options.h', + 'remix_api.h' ]) thread_dep = dependency('threads') @@ -44,13 +46,11 @@ server_resource = windows.compile_resources('NvRemixBridge.rc', depend_files : ['NvRemixBridge.ico'] ) -remixapi_include_path = include_directories('../api') - server_exe = executable('NvRemixBridge', server_src, server_header, server_version, server_resource, sources : [ bridge_version ], build_by_default : (cpu_family == 'x86_64') ? true : false, dependencies : [ thread_dep, util_dep, lib_version, tracy_dep ], -include_directories : [ bridge_include_path, util_include_path, remixapi_include_path ], +include_directories : [ bridge_include_path, util_include_path, public_include_path, ext_include_path ], win_subsystem : 'windows') diff --git a/src/server/module_processing.cpp b/src/server/module_processing.cpp index 5a5e583..d79e814 100644 --- a/src/server/module_processing.cpp +++ b/src/server/module_processing.cpp @@ -4,6 +4,8 @@ #include "module_processing.h" +#include "remix_api.h" + #include "util_bridge_assert.h" #include "util_modulecommand.h" @@ -26,25 +28,6 @@ extern std::unordered_map gpD3DSwapChains; extern std::mutex gLock; -namespace remix_api { - remixapi_Interface g_remix = {}; - bool g_remix_initialized = false; - HMODULE g_remix_dll = nullptr; - IDirect3DDevice9Ex* g_device = nullptr; - std::mutex g_device_mutex; - - IDirect3DDevice9Ex* getDevice() { - std::scoped_lock device_lock(g_device_mutex); - if (g_device) { - Logger::info("[RemixApi] getDevice(): success"); - return g_device; - } - - Logger::info("[RemixApi] getDevice(): failed"); - return nullptr; - } -} - #define PULL(type, name) const auto& name = (type)ModuleBridge::get_data() #define PULL_I(name) PULL(INT, name) #define PULL_U(name) PULL(UINT, name) @@ -385,7 +368,7 @@ void processModuleCommandQueue(std::atomic* const pbSignalEnd) { } else { Logger::info("Server side D3D9 DeviceEx created successfully!"); gpD3DDevices[pHandle] = pD3DDevice; - remix_api::g_device = pD3DDevice; + remixapi::g_device = pD3DDevice; } // Send response back to the client @@ -417,7 +400,7 @@ void processModuleCommandQueue(std::atomic* const pbSignalEnd) { } else { Logger::info("Server side D3D9 Device created successfully!"); gpD3DDevices[pHandle] = (IDirect3DDevice9Ex*) pD3DDevice; - remix_api::g_device = (IDirect3DDevice9Ex*) pD3DDevice; + remixapi::g_device = (IDirect3DDevice9Ex*) pD3DDevice; } // Send response back to the client diff --git a/src/server/module_processing.h b/src/server/module_processing.h index 065e757..528e307 100644 --- a/src/server/module_processing.h +++ b/src/server/module_processing.h @@ -2,16 +2,4 @@ #include -#include "remix_api/remix_c.h" -#include - void processModuleCommandQueue(std::atomic* const bSignalEnd); - -namespace remix_api { - extern remixapi_Interface g_remix; - extern bool g_remix_initialized; - extern HMODULE g_remix_dll; - extern IDirect3DDevice9Ex* g_device; - extern std::mutex g_device_mutex; - extern IDirect3DDevice9Ex* getDevice(); -} diff --git a/src/server/remix_api.cpp b/src/server/remix_api.cpp new file mode 100644 index 0000000..97f0e18 --- /dev/null +++ b/src/server/remix_api.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. + * + * 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 "remix_api.h" + +#include "log/log.h" + +namespace remixapi { + remixapi_Interface g_remix = {}; + bool g_remix_initialized = false; + HMODULE g_remix_dll = nullptr; + IDirect3DDevice9Ex* g_device = nullptr; + std::mutex g_device_mutex; + + IDirect3DDevice9Ex* getDevice() { + std::scoped_lock device_lock(g_device_mutex); + if (g_device) { + bridge_util::Logger::info("[RemixApi] getDevice(): success"); + return g_device; + } + + bridge_util::Logger::info("[RemixApi] getDevice(): failed"); + return nullptr; + } +} diff --git a/src/server/remix_api.h b/src/server/remix_api.h new file mode 100644 index 0000000..c7b4a46 --- /dev/null +++ b/src/server/remix_api.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ +#pragma once + +#include + +#include "util_devicecommand.h" +#include "util_remixapi.h" + +#include + +namespace remixapi { + extern remixapi_Interface g_remix; + extern bool g_remix_initialized; + extern HMODULE g_remix_dll; + extern IDirect3DDevice9Ex* g_device; + extern std::mutex g_device_mutex; + extern IDirect3DDevice9Ex* getDevice(); + + static inline remixapi_StructType pullSType() { + return (remixapi_StructType) DeviceBridge::get_data(); + } + + static inline util::HandleUID pullHandle() { + return DeviceBridge::get_data(); + } + + static inline bool pullBool() { + const auto boolVal = (Bool)DeviceBridge::get_data(); + // Since standalone values are pushed/pulled at a DWORD resolution + // we must mask out the lowest-most byte + bool b = (bool)((uint32_t)boolVal & 0x00ff); + return b; + } +} diff --git a/src/util/config/global_options.h b/src/util/config/global_options.h index 5930c89..ac93cf7 100644 --- a/src/util/config/global_options.h +++ b/src/util/config/global_options.h @@ -243,6 +243,11 @@ class GlobalOptions { static const bool getAlwaysCopyEntireStaticBuffer() { return get().alwaysCopyEntireStaticBuffer; } + + + static bool getExposeRemixApi() { + return get().exposeRemixApi; + } private: GlobalOptions() = default; @@ -414,6 +419,8 @@ class GlobalOptions { // If set and a buffer is not dynamic, vertex and index buffer lock/unlocks will ignore the bounds set during the lock call // and the brifge will copy the entire buffer. This means alwaysCopyEntireStaticBuffer = bridge_util::Config::getOption("alwaysCopyEntireStaticBuffer", false); + + exposeRemixApi = bridge_util::Config::getOption("exposeRemixApi", false); } void initSharedHeapPolicy(); @@ -464,4 +471,5 @@ class GlobalOptions { uint32_t sharedHeapFreeChunkWaitTimeout; uint32_t threadSafetyPolicy; bool alwaysCopyEntireStaticBuffer; + bool exposeRemixApi; }; diff --git a/src/util/log/log.cpp b/src/util/log/log.cpp index 45f11dc..90bdd29 100644 --- a/src/util/log/log.cpp +++ b/src/util/log/log.cpp @@ -20,6 +20,7 @@ * DEALINGS IN THE SOFTWARE. */ #include "log.h" +#include "log_strings.h" #include "util_filesys.h" @@ -131,6 +132,12 @@ namespace bridge_util { get().emitMsg(LogLevel::Error, message); } + void Logger::errLogMessageBoxAndExit(const std::string& message) { + Logger::err(message); + MessageBox(nullptr, message.c_str(), logger_strings::RtxRemixRuntimeError, MB_OK | MB_TOPMOST | MB_TASKMODAL); + std::exit(-1 ); + } + void Logger::log(const LogLevel level, const std::string& message) { get().emitMsg(level, message); } diff --git a/src/util/log/log.h b/src/util/log/log.h index 6c2139a..47be607 100644 --- a/src/util/log/log.h +++ b/src/util/log/log.h @@ -52,6 +52,7 @@ namespace bridge_util { static void info(const std::string& message); static void warn(const std::string& message); static void err(const std::string& message); + static void errLogMessageBoxAndExit(const std::string& message); static void log(const LogLevel level, const std::string& message); // The lowest level method. NOT thread-safe. Use at your own risk! static void logLine(const LogLevel level, const char* line); diff --git a/src/util/log/log_strings.h b/src/util/log/log_strings.h new file mode 100644 index 0000000..d8d5cb4 --- /dev/null +++ b/src/util/log/log_strings.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ +#pragma once + +#include +#include + +namespace logger_strings { + constexpr char* OutOfBufferMemory = "The host application has tried to write data larger than one of the RTX Remix Bridge's buffers. Increase one of the buffer sizes in \".trex\\bridge.conf\".\n\n"; + constexpr char* OutOfBufferMemory1 = " Buffer Option: "; + constexpr char* MultipleActiveCommands = "Multiple active Command instances detected!"; + constexpr char* RtxRemixRuntimeError = "RTX Remix Runtime Error!"; + constexpr char* BridgeClientClosing = "The bridge server process (.trex\\NvRemixBridge.exe) or dxvk-remix (.trex\\d3d9.dll) has unexpectedly exited, closing the bridge client and host application."; + + inline static const std::map bufferNameToOptionMap = + { + {"ModuleClient2ServerData", "moduleClientChannelMemSize"}, + {"ModuleServer2ClientData", "moduleServerChannelMemSize"}, + {"DeviceClient2ServerData", "clientChannelMemSize"}, + {"DeviceServer2ClientData", "serverChannelMemSize"} + }; + + static inline std::string bufferNameToOption(std::string name) { + std::string optionName = ""; + auto it = logger_strings::bufferNameToOptionMap.find(name); + if (it != logger_strings::bufferNameToOptionMap.end()) { + optionName = it->second; + } + return optionName; + } + +} \ No newline at end of file diff --git a/src/util/meson.build b/src/util/meson.build index 9eed028..d3d8e2e 100644 --- a/src/util/meson.build +++ b/src/util/meson.build @@ -25,6 +25,7 @@ util_src = files([ 'util_gdi.cpp', 'util_messagechannel.cpp', 'util_process.cpp', + 'util_remixapi.cpp', 'util_seh.cpp', 'util_semaphore.cpp', 'util_sharedheap.cpp', @@ -56,9 +57,11 @@ util_header = files([ 'util_messagechannel.h', 'util_once.h', 'util_process.h', + 'util_remixapi.h', 'util_scopedlock.h', 'util_seh.h', 'util_semaphore.h', + 'util_serializable.h', 'util_serializer.h', 'util_sharedmemory.h', 'util_singleton.h', @@ -66,12 +69,13 @@ util_header = files([ 'util_version.h', 'util_monitor.h', 'log/log.h', + 'log/log_strings.h', 'config/config.h', 'config/global_options.h', ]) util_lib = static_library('util', util_src, util_header, - include_directories : [ bridge_include_path ], + include_directories : [ bridge_include_path, public_include_path, ext_include_path ], ) util_dep = declare_dependency( diff --git a/src/util/util_bridgecommand.cpp b/src/util/util_bridgecommand.cpp index 3b52f51..7c866ac 100644 --- a/src/util/util_bridgecommand.cpp +++ b/src/util/util_bridgecommand.cpp @@ -20,6 +20,7 @@ * DEALINGS IN THE SOFTWARE. */ #include "util_bridgecommand.h" +#include "log/log_strings.h" namespace { DWORD get_default_timeout() { @@ -67,8 +68,7 @@ DECL_BRIDGE_FUNC(void, syncDataQueue, size_t expectedMemUsage, bool posResetOnLa Logger::warn("Data Queue overwrite condition triggered"); // Check to see if there is even enough space to ever succeed in pushing all the data if ((expectedMemUsage + (currClientDataPos >= s_curBatchStartPos ? currClientDataPos - s_curBatchStartPos : currClientDataPos + totalSize - s_curBatchStartPos)) > totalSize) { - Logger::err("Command's data batch size is too large and overwrite could not be prevented!"); - throw std::exception("Command's data batch size is too large and overwrite could not be prevented!"); + Logger::errLogMessageBoxAndExit(std::string(logger_strings::OutOfBufferMemory) + std::string(logger_strings::OutOfBufferMemory1) + logger_strings::bufferNameToOption(s_pWriterChannel->data->getName())); } // Wait for the server to access the data at the above postion const auto maxRetries = GlobalOptions::getCommandRetries(); @@ -281,8 +281,7 @@ DECL_COMMAND_FUNC(,Command,const Commands::D3D9Command command, assert(!s_pWriterChannel->pbCmdInProgress->load()); if (s_pWriterChannel->pbCmdInProgress->load()) { - Logger::err("Multiple active Command instances detected!"); - throw std::exception("Multiple active Command instances detected!"); + Logger::errLogMessageBoxAndExit(logger_strings::MultipleActiveCommands); } // Only start a data batch if the bridge is actually enabled, otherwise this becomes a no-op if (gbBridgeRunning) { diff --git a/src/util/util_circularbuffer.h b/src/util/util_circularbuffer.h index bb4a10f..79310b6 100644 --- a/src/util/util_circularbuffer.h +++ b/src/util/util_circularbuffer.h @@ -24,8 +24,11 @@ #include #include +#include #include "util_circularqueue.h" +#include "log/log_strings.h" +#include "log/log.h" namespace bridge_util { @@ -130,7 +133,8 @@ namespace bridge_util { size_t space_needed = chunk_size(size); if (m_pos + space_needed >= m_size) { if (space_needed > m_size) { - throw std::exception("The data is larger than shared memory size!"); + // FATAL Condition, Message User and Exit + Logger::errLogMessageBoxAndExit(std::string(logger_strings::OutOfBufferMemory) + std::string(logger_strings::OutOfBufferMemory1) + logger_strings::bufferNameToOption(m_name)); } // Roll over immediately if not enough space left m_pos = 0; diff --git a/src/util/util_circularqueue.h b/src/util/util_circularqueue.h index 21d7954..e4a513f 100644 --- a/src/util/util_circularqueue.h +++ b/src/util/util_circularqueue.h @@ -42,6 +42,7 @@ namespace bridge_util { protected: T* m_data; + const std::string m_name; const size_t m_size; const size_t m_queueSize; const Accessor m_access; @@ -53,7 +54,8 @@ namespace bridge_util { public: CircularQueue(const std::string& name, Accessor access, void* pMemory, const size_t memSize, const size_t queueSize) - : m_size(memSize / sizeof(T)) + : m_name(name) + , m_size(memSize / sizeof(T)) , m_access(access) , m_queueSize(queueSize) , m_data(nullptr) // INIT @@ -71,6 +73,10 @@ namespace bridge_util { ~CircularQueue() { } + const std::string& getName() { + return m_name; + } + // Push object to queue Result push(const T obj) { if (m_batchInProgress) { diff --git a/src/util/util_commands.h b/src/util/util_commands.h index 091ea63..a9b6b2d 100644 --- a/src/util/util_commands.h +++ b/src/util/util_commands.h @@ -38,23 +38,17 @@ namespace Commands { Bridge_Response, Bridge_DebugMessage, - Api_DebugPrint, - Api_CreateOpaqueMaterial, - Api_CreateTranslucentMaterial, - Api_CreatePortalMaterial, - Api_DestroyMaterial, - Api_CreateTriangleMesh, - Api_DestroyMesh, - Api_DrawMeshInstance, - Api_CreateSphereLight, - Api_CreateRectLight, - Api_CreateDiskLight, - Api_CreateCylinderLight, - Api_CreateDistantLight, - Api_DestroyLight, - Api_DrawLightInstance, - Api_SetConfigVariable, - Api_RegisterDevice, + RemixApi_CreateMaterial, + RemixApi_DestroyMaterial, + RemixApi_CreateMesh, + RemixApi_DestroyMesh, + RemixApi_DrawInstance, + RemixApi_CreateLight, + RemixApi_DestroyLight, + RemixApi_DrawLightInstance, + RemixApi_SetConfigVariable, + RemixApi_CreateD3D9, + RemixApi_RegisterDevice, Bridge_SharedHeap_AddSeg, Bridge_SharedHeap_Alloc, @@ -482,23 +476,17 @@ namespace Commands { case Bridge_Response: return "Response"; case Bridge_DebugMessage: return "DebugMessage"; - case Api_DebugPrint: return "ApiDebugPrint"; - case Api_CreateOpaqueMaterial: return "ApiCreateOpaqueMaterial"; - case Api_CreateTranslucentMaterial: return "ApiCreateTranslucentMaterial"; - case Api_CreatePortalMaterial: return "ApiCreatePortalMaterial"; - case Api_DestroyMaterial: return "ApiDestroyMaterial"; - case Api_CreateTriangleMesh: return "ApiCreateTriangleMesh"; - case Api_DestroyMesh: return "ApiDestroyMesh"; - case Api_DrawMeshInstance: return "ApiDrawMeshInstance"; - case Api_CreateSphereLight: return "ApiCreateSphereLight"; - case Api_CreateRectLight: return "ApiCreateRectLight"; - case Api_CreateDiskLight: return "ApiCreateDiskLight"; - case Api_CreateCylinderLight: return "ApiCreateCylinderLight"; - case Api_CreateDistantLight: return "ApiCreateDistantLight"; - case Api_DestroyLight: return "ApiDestroyLight"; - case Api_DrawLightInstance: return "DrawLightInstance"; - case Api_SetConfigVariable: return "ApiSetConfigVariable"; - case Api_RegisterDevice: return "ApiRegisterDevice"; + case RemixApi_CreateMaterial: return "RemixApi_CreateMaterial"; + case RemixApi_DestroyMaterial: return "RemixApi_DestroyMaterial"; + case RemixApi_CreateMesh: return "RemixApi_CreateMesh"; + case RemixApi_DestroyMesh: return "RemixApi_DestroyMesh"; + case RemixApi_DrawInstance: return "RemixApi_DrawInstance"; + case RemixApi_CreateLight: return "RemixApi_CreateLight"; + case RemixApi_DestroyLight: return "RemixApi_DestroyLight"; + case RemixApi_DrawLightInstance: return "DrawLightInstance"; + case RemixApi_SetConfigVariable: return "RemixApi_SetConfigVariable"; + case RemixApi_CreateD3D9: return "RemixApi_CreateD3D9"; + case RemixApi_RegisterDevice: return "RemixApi_RegisterDevice"; case Bridge_SharedHeap_AddSeg: return "SharedHeap_AddSeg"; case Bridge_SharedHeap_Alloc: return "SharedHeap_Alloc"; diff --git a/src/util/util_remixapi.cpp b/src/util/util_remixapi.cpp new file mode 100644 index 0000000..4d231f1 --- /dev/null +++ b/src/util/util_remixapi.cpp @@ -0,0 +1,773 @@ +/* + * Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. + * + * 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 "util_remixapi.h" + +#include +#include +#include +#include +#include + +using namespace remixapi::util::serialize; + +namespace { + +// Convenience function templates to help with the boilerplate necessary +// to handle deserializing the `const T*` RemixApi struct member pattern + +template +static inline void deserialize_const_p(void*& pDeserialize, const T*& deserializeTo, const uint32_t size) { + T* new_arr = (T*) new uint8_t[size]; + bridge_util::deserialize(pDeserialize, new_arr, size); + deserializeTo = new_arr; +} + +template +static inline void deserialize_const_p_for_each(void*& pDeserialize, const T*& deserializeTo, const size_t num) { + T* new_arr = (T*) new T[num]; + for(size_t i = 0; i < num; ++i) { + bridge_util::deserialize(pDeserialize, new_arr[i]); + } + deserializeTo = new_arr; +} + +} + +namespace bridge_util { + +// Types that should de-/serialize out of the box, because +// they only contain a single member which should be aligned +// with the start of the struct + +// remixapi_Rect2D +template<> +static inline constexpr uint32_t sizeOf() { + return 4 * sizeof(int32_t); +} + +// remixapi_Float2D +template<> +static inline constexpr uint32_t sizeOf() { + return 2 * sizeof(float); +} + +// remixapi_Float3D +template<> +static inline constexpr uint32_t sizeOf() { + return 3 * sizeof(float); +} + +//remixapi_Float4D +template<> +static inline constexpr uint32_t sizeOf() { + return 4 * sizeof(float); +} + +// remixapi_Transform +template<> +static inline constexpr uint32_t sizeOf() { + return 3 * 4 * sizeof(float); +} + +// remixapi_Path +template<> +static inline uint32_t sizeOf(const remixapi_Path& path) { + return (wcslen(path) + 1) * sizeof(wchar_t); +} +template<> +void serialize(const remixapi_Path& serializeFrom, void*& pSerialize) { + serialize(serializeFrom, pSerialize, sizeOf(serializeFrom)); +} +template<> +void deserialize(void*& deserializeFrom, remixapi_Path& deserializeTo) { + const uint32_t size = sizeOf(reinterpret_cast(deserializeFrom)); + assert(size <= MAX_PATH); + auto intermediate = new wchar_t[size]; + deserialize(deserializeFrom, intermediate, size); + deserializeTo = intermediate; +} + +// remixapi_HardcodedVertex +template<> +static inline constexpr uint32_t sizeOf() { + return sizeof(remixapi_HardcodedVertex::position) + + sizeof(remixapi_HardcodedVertex::normal) + + sizeof(remixapi_HardcodedVertex::texcoord) + + sizeof(remixapi_HardcodedVertex::color); +} +template<> +void serialize(const remixapi_HardcodedVertex& serializeFrom, void*& pSerialize) { + serialize(&serializeFrom.position[0], pSerialize, sizeOf(serializeFrom.position)); + serialize(&serializeFrom.normal[0], pSerialize, sizeOf(serializeFrom.normal)); + serialize(&serializeFrom.texcoord[0], pSerialize, sizeOf(serializeFrom.texcoord)); + serialize(&serializeFrom.color, pSerialize, sizeOf(serializeFrom.color)); +} +template<> +void deserialize(void*& deserializeFrom, remixapi_HardcodedVertex& deserializeTo) { + deserialize(deserializeFrom, &deserializeTo.position[0], sizeOf(deserializeTo.position)); + deserialize(deserializeFrom, &deserializeTo.normal[0], sizeOf(deserializeTo.normal)); + deserialize(deserializeFrom, &deserializeTo.texcoord[0], sizeOf(deserializeTo.texcoord)); + deserialize(deserializeFrom, &deserializeTo.color, sizeOf(deserializeTo.color)); +} + +// remixapi_*Handles +// convenience macro to define same specializations for all three types +#define REMIX_API_HANDLE_FUNCS(HandleT) \ +template<> \ +static inline constexpr uint32_t sizeOf() { \ + return sizeof(uint32_t); \ +} \ +template<> \ +void serialize(const HandleT& serializeFrom, void*& serializeTo) { \ + serialize(&serializeFrom, serializeTo, sizeof(uint32_t)); \ +} \ +template<> \ +void deserialize(void*& deserializeFrom, HandleT& deserializeTo) { \ + deserializeTo = nullptr; \ + deserialize(deserializeFrom, &deserializeTo, sizeof(uint32_t)); \ +} +REMIX_API_HANDLE_FUNCS(remixapi_MaterialHandle) +REMIX_API_HANDLE_FUNCS(remixapi_MeshHandle) +REMIX_API_HANDLE_FUNCS(remixapi_LightHandle) + + +////////////////// +// MaterialInfo // +////////////////// + +#define MaterialInfoVars sType, \ + hash, \ + albedoTexture, \ + normalTexture, \ + tangentTexture, \ + emissiveTexture, \ + emissiveIntensity, \ + emissiveColorConstant, \ + spriteSheetRow, \ + spriteSheetCol, \ + spriteSheetFps, \ + filterMode, \ + wrapModeU, \ + wrapModeV +uint32_t MaterialInfo::_calcSize() const { + return fold_helper::calcSize(MaterialInfoVars); +} +void MaterialInfo::_serialize(void*& pSerialize) const { + fold_helper::serialize(pSerialize, MaterialInfoVars); +} +void MaterialInfo::_deserialize(void*& pDeserialize) { + pNext = nullptr; + fold_helper::deserialize(pDeserialize, MaterialInfoVars); +} +void MaterialInfo::_dtor() { + delete albedoTexture; + delete normalTexture; + delete tangentTexture; + delete emissiveTexture; +} + + +#define MaterialInfoOpaqueVars sType, \ + roughnessTexture, \ + metallicTexture, \ + anisotropy, \ + albedoConstant, \ + opacityConstant, \ + roughnessConstant, \ + metallicConstant, \ + thinFilmThickness_hasvalue, \ + thinFilmThickness_value, \ + alphaIsThinFilmThickness, \ + heightTexture, \ + heightTextureStrength, \ + useDrawCallAlphaState, \ + blendType_hasvalue, \ + blendType_value, \ + invertedBlend, \ + alphaTestType, \ + alphaReferenceValue +uint32_t MaterialInfoOpaque::_calcSize() const { + return fold_helper::calcSize(MaterialInfoOpaqueVars); +} +void MaterialInfoOpaque::_serialize(void*& pSerialize) const { + fold_helper::serialize(pSerialize, MaterialInfoOpaqueVars); +} +void MaterialInfoOpaque::_deserialize(void*& pDeserialize) { + pNext = nullptr; + fold_helper::deserialize(pDeserialize, MaterialInfoOpaqueVars); +} +void MaterialInfoOpaque::_dtor() { + delete roughnessTexture; + delete metallicTexture; + delete heightTexture; +} + + +#define MaterialInfoOpaqueSubsurfaceVars sType, \ + subsurfaceTransmittanceTexture, \ + subsurfaceThicknessTexture, \ + subsurfaceSingleScatteringAlbedoTexture, \ + subsurfaceTransmittanceColor, \ + subsurfaceMeasurementDistance, \ + subsurfaceSingleScatteringAlbedo, \ + subsurfaceVolumetricAnisotropy +uint32_t MaterialInfoOpaqueSubsurface::_calcSize() const { + return fold_helper::calcSize(MaterialInfoOpaqueSubsurfaceVars); +} +void MaterialInfoOpaqueSubsurface::_serialize(void*& pSerialize) const { + fold_helper::serialize(pSerialize, MaterialInfoOpaqueSubsurfaceVars); +} +void MaterialInfoOpaqueSubsurface::_deserialize(void*& pDeserialize) { + pNext = nullptr; + fold_helper::deserialize(pDeserialize, MaterialInfoOpaqueSubsurfaceVars); +} +void MaterialInfoOpaqueSubsurface::_dtor() { + delete subsurfaceTransmittanceTexture; + delete subsurfaceThicknessTexture; + delete subsurfaceSingleScatteringAlbedoTexture; +} + + +#define MaterialInfoTranslucentVars sType, \ + transmittanceTexture, \ + refractiveIndex, \ + transmittanceColor, \ + transmittanceMeasurementDistance, \ + thinWallThickness_hasvalue, \ + thinWallThickness_value, \ + useDiffuseLayer +uint32_t MaterialInfoTranslucent::_calcSize() const { + return fold_helper::calcSize(MaterialInfoTranslucentVars); +} +void MaterialInfoTranslucent::_serialize(void*& pSerialize) const { + fold_helper::serialize(pSerialize, MaterialInfoTranslucentVars); +} +void MaterialInfoTranslucent::_deserialize(void*& pDeserialize) { + pNext = nullptr; + fold_helper::deserialize(pDeserialize, MaterialInfoTranslucentVars); +} +void MaterialInfoTranslucent::_dtor() { + delete transmittanceTexture; +} + + +#define MaterialInfoPortalVars sType, \ + rayPortalIndex, \ + rotationSpeed +uint32_t MaterialInfoPortal::_calcSize() const { + return fold_helper::calcSize(MaterialInfoPortalVars); +} +void MaterialInfoPortal::_serialize(void*& pSerialize) const { + fold_helper::serialize(pSerialize, MaterialInfoPortalVars); +} +void MaterialInfoPortal::_deserialize(void*& pDeserialize) { + pNext = nullptr; + fold_helper::deserialize(pDeserialize, MaterialInfoPortalVars); +} +void MaterialInfoPortal::_dtor() { +} + +////////////// +// MeshInfo // +////////////// + +uint32_t MeshInfo::_calcSize() const; +template<> +static inline uint32_t sizeOf(const remixapi_MeshInfoSurfaceTriangles& surface); + +void MeshInfo::_serialize(void*& pSerialize) const; +template<> +void serialize(const remixapi_MeshInfoSurfaceTriangles& surface, void*& pSerialize); + +void MeshInfo::_deserialize(void*& pDeserialize); +template<> +void deserialize(void*& pDeserialize, remixapi_MeshInfoSurfaceTriangles& surface); + + +uint32_t MeshInfo::_calcSize() const { + uint32_t size = 0; + size += sizeOf(sType); + size += sizeOf(hash); + size += sizeOf(surfaces_count); + for(size_t nSurface = 0; nSurface < surfaces_count; ++nSurface) { + size += sizeOf(surfaces_values[nSurface]); + } + return size; +} + +static inline uint32_t blendWeightSizePerVtx(const remixapi_MeshInfoSkinning& skinning) { + return skinning.blendWeights_count * skinning.bonesPerVertex * sizeof(float); +} +static inline uint32_t blendIndicesSizePerVtx(const remixapi_MeshInfoSkinning& skinning) { + return skinning.blendIndices_count * skinning.bonesPerVertex * sizeof(uint32_t); +} +template<> +static inline uint32_t sizeOf(const remixapi_MeshInfoSurfaceTriangles& surface) { + uint32_t size = 0; + size += sizeOf(surface.vertices_count); + size += surface.vertices_count * sizeOf(*surface.vertices_values); + size += sizeOf(surface.indices_count); + size += surface.indices_count * sizeOf(*surface.indices_values); + size += sizeOf(surface.skinning_hasvalue); + if(surface.skinning_hasvalue) { + size += sizeOf(surface.skinning_value.bonesPerVertex); + size += sizeOf(surface.skinning_value.blendWeights_count); + size += surface.vertices_count * blendWeightSizePerVtx(surface.skinning_value); + size += sizeOf(surface.skinning_value.blendIndices_count); + size += surface.vertices_count * blendIndicesSizePerVtx(surface.skinning_value); + } + size += sizeof(uint32_t); + return size; +} + +void MeshInfo::_serialize(void*& pSerialize) const { + bridge_util::serialize(sType, pSerialize); + bridge_util::serialize(hash, pSerialize); + bridge_util::serialize(surfaces_count, pSerialize); + for(size_t nSurface = 0; nSurface < surfaces_count; ++nSurface) { + bridge_util::serialize(surfaces_values[nSurface], pSerialize); + } +} + +template<> +void serialize(const remixapi_MeshInfoSurfaceTriangles& surface, void*& pSerialize) { + // Vtxs + bridge_util::serialize(surface.vertices_count, pSerialize); + for(size_t iVtx = 0; iVtx < surface.vertices_count; ++iVtx) { + bridge_util::serialize(surface.vertices_values[iVtx], pSerialize); + } + // Idxs + bridge_util::serialize(surface.indices_count, pSerialize); + const size_t indicesSize = surface.indices_count * sizeof(uint32_t); + bridge_util::serialize(surface.indices_values, pSerialize, indicesSize); + // Skinning + bridge_util::serialize(surface.skinning_hasvalue, pSerialize); + if(surface.skinning_hasvalue) { + const auto& skinning = surface.skinning_value; + bridge_util::serialize(skinning.bonesPerVertex, pSerialize); + // Blend Weights + bridge_util::serialize(skinning.blendWeights_count, pSerialize); + const size_t blendWeightsSize = surface.vertices_count * blendWeightSizePerVtx(skinning); + bridge_util::serialize(skinning.blendWeights_values, pSerialize, blendWeightsSize); + // Blend Indices + bridge_util::serialize(skinning.blendIndices_count, pSerialize); + const size_t blendIndicesSize = surface.vertices_count * blendIndicesSizePerVtx(skinning); + bridge_util::serialize(skinning.blendIndices_values, pSerialize, blendIndicesSize); + } + bridge_util::serialize(surface.material, pSerialize); +} + +void MeshInfo::_deserialize(void*& pDeserialize) { + bridge_util::deserialize(pDeserialize, sType); + bridge_util::deserialize(pDeserialize, hash); + bridge_util::deserialize(pDeserialize, surfaces_count); + deserialize_const_p_for_each(pDeserialize, surfaces_values, surfaces_count); +} + +template<> +void deserialize(void*& pDeserialize, remixapi_MeshInfoSurfaceTriangles& surface) { + // Vtxs + bridge_util::deserialize(pDeserialize, surface.vertices_count); + deserialize_const_p_for_each(pDeserialize, surface.vertices_values, surface.vertices_count); + // Idxs + bridge_util::deserialize(pDeserialize, surface.indices_count); + const size_t indicesSize = surface.indices_count * sizeof(uint32_t); + deserialize_const_p(pDeserialize, surface.indices_values, indicesSize); + // Skinning + bridge_util::deserialize(pDeserialize, surface.skinning_hasvalue); + if(surface.skinning_hasvalue) { + auto& skinning = surface.skinning_value; + bridge_util::deserialize(pDeserialize, skinning.bonesPerVertex); + // Blend Weights + bridge_util::deserialize(pDeserialize, skinning.blendWeights_count); + const size_t blendWeightsSize = surface.vertices_count * blendWeightSizePerVtx(skinning); + deserialize_const_p(pDeserialize, skinning.blendWeights_values, blendWeightsSize); + // Blend Indices + bridge_util::deserialize(pDeserialize, skinning.blendIndices_count); + const size_t blendIndicesSize = surface.vertices_count * blendIndicesSizePerVtx(skinning); + deserialize_const_p(pDeserialize, skinning.blendIndices_values, blendIndicesSize); + } + bridge_util::deserialize(pDeserialize, surface.material); +} + +void MeshInfo::_dtor() { + for(size_t nSurface = 0; nSurface < surfaces_count; ++nSurface) { + auto& surface = surfaces_values[nSurface]; + delete surface.vertices_values; + delete surface.indices_values; + if(surface.skinning_hasvalue) { + auto& skinning = surface.skinning_value; + delete skinning.blendWeights_values; + delete skinning.blendIndices_values; + } + } +} + +////////////////// +// InstanceInfo // +////////////////// +#define InstanceInfoVars sType, \ + categoryFlags, \ + mesh, \ + transform, \ + doubleSided +uint32_t InstanceInfo::_calcSize() const { + return fold_helper::calcSize(InstanceInfoVars); +} +void InstanceInfo::_serialize(void*& pSerialize) const { + fold_helper::serialize(pSerialize, InstanceInfoVars); +} +void InstanceInfo::_deserialize(void*& pDeserialize) { + pNext = nullptr; + fold_helper::deserialize(pDeserialize, InstanceInfoVars); +} +void InstanceInfo::_dtor() { +} + + +#define InstanceInfoObjectPickingVars objectPickingValue +uint32_t InstanceInfoObjectPicking::_calcSize() const { + return fold_helper::calcSize(InstanceInfoObjectPickingVars); +} +void InstanceInfoObjectPicking::_serialize(void*& pSerialize) const { + fold_helper::serialize(pSerialize, InstanceInfoObjectPickingVars); +} +void InstanceInfoObjectPicking::_deserialize(void*& pDeserialize) { + pNext = nullptr; + fold_helper::deserialize(pDeserialize, InstanceInfoObjectPickingVars); +} +void InstanceInfoObjectPicking::_dtor() { +} + + +#define InstanceInfoBlendVars sType, \ + alphaTestEnabled, \ + alphaTestReferenceValue, \ + alphaTestCompareOp, \ + alphaBlendEnabled, \ + srcColorBlendFactor, \ + dstColorBlendFactor, \ + colorBlendOp, \ + textureColorArg1Source, \ + textureColorArg2Source, \ + textureColorOperation, \ + textureAlphaArg1Source, \ + textureAlphaArg2Source, \ + textureAlphaOperation, \ + tFactor, \ + isTextureFactorBlend +uint32_t InstanceInfoBlend::_calcSize() const { + return fold_helper::calcSize(InstanceInfoBlendVars); +} +void InstanceInfoBlend::_serialize(void*& pSerialize) const { + fold_helper::serialize(pSerialize, InstanceInfoBlendVars); +} +void InstanceInfoBlend::_deserialize(void*& pDeserialize) { + pNext = nullptr; + fold_helper::deserialize(pDeserialize, InstanceInfoBlendVars); +} +void InstanceInfoBlend::_dtor() { +} + + +uint32_t InstanceInfoTransforms::_calcSize() const { + uint32_t size = 0; + size += sizeOf(sType); + size += sizeOf(boneTransforms_count); + size += sizeOf() * boneTransforms_count; + return size; +} +void InstanceInfoTransforms::_serialize(void*& pSerialize) const { + bridge_util::serialize(sType, pSerialize); + bridge_util::serialize(boneTransforms_count, pSerialize); + for(size_t iXform = 0; iXform < boneTransforms_count; ++iXform) { + bridge_util::serialize(boneTransforms_values[iXform], pSerialize); + } +} +void InstanceInfoTransforms::_deserialize(void*& pDeserialize) { + bridge_util::deserialize(pDeserialize, sType); + pNext = nullptr; + bridge_util::deserialize(pDeserialize, boneTransforms_count); + deserialize_const_p_for_each(pDeserialize, boneTransforms_values, boneTransforms_count); +} +void InstanceInfoTransforms::_dtor() { + delete boneTransforms_values; +} + + +/////////////// +// LightInfo // +/////////////// + +#define LightInfoVars sType, \ + hash, \ + radiance +uint32_t LightInfo::_calcSize() const { + return fold_helper::calcSize(LightInfoVars); +} +void LightInfo::_serialize(void*& pSerialize) const { + fold_helper::serialize(pSerialize, LightInfoVars); +} +void LightInfo::_deserialize(void*& pDeserialize) { + pNext = nullptr; + fold_helper::deserialize(pDeserialize, LightInfoVars); +} +void LightInfo::_dtor() { +} + + +#define LightShapingVars shaping.direction, \ + shaping.coneAngleDegrees, \ + shaping.coneSoftness, \ + shaping.focusExponent +template<> +static inline uint32_t sizeOf(const remixapi_LightInfoLightShaping& shaping) { + return fold_helper::calcSize(LightShapingVars); +} +template<> +void serialize(const remixapi_LightInfoLightShaping& shaping, void*& pSerialize) { + fold_helper::serialize(pSerialize, LightShapingVars); +} +template<> +void deserialize(void*& pDeserialize, remixapi_LightInfoLightShaping& shaping) { + fold_helper::deserialize(pDeserialize, LightShapingVars); +} + + +#define LightInfoSphereVars sType, \ + position, \ + radius, \ + shaping_hasvalue, \ + shaping_value +uint32_t LightInfoSphere::_calcSize() const { + return fold_helper::calcSize(LightInfoSphereVars); +} +void LightInfoSphere::_serialize(void*& pSerialize) const { + fold_helper::serialize(pSerialize, LightInfoSphereVars); +} +void LightInfoSphere::_deserialize(void*& pDeserialize) { + pNext = nullptr; + fold_helper::deserialize(pDeserialize, LightInfoSphereVars); +} +void LightInfoSphere::_dtor() { +} + + +#define LightInfoRectVars sType, \ + position, \ + xAxis, \ + xSize, \ + yAxis, \ + ySize, \ + direction, \ + shaping_hasvalue, \ + shaping_value +uint32_t LightInfoRect::_calcSize() const { + return fold_helper::calcSize(LightInfoRectVars); +} +void LightInfoRect::_serialize(void*& pSerialize) const { + fold_helper::serialize(pSerialize, LightInfoRectVars); +} +void LightInfoRect::_deserialize(void*& pDeserialize) { + pNext = nullptr; + fold_helper::deserialize(pDeserialize, LightInfoRectVars); +} +void LightInfoRect::_dtor() { +} + + +#define LightInfoDiskVars sType, \ + position, \ + xAxis, \ + xRadius, \ + yAxis, \ + yRadius, \ + direction, \ + shaping_hasvalue, \ + shaping_value +uint32_t LightInfoDisk::_calcSize() const { + return fold_helper::calcSize(LightInfoDiskVars); +} +void LightInfoDisk::_serialize(void*& pSerialize) const { + fold_helper::serialize(pSerialize, LightInfoDiskVars); +} +void LightInfoDisk::_deserialize(void*& pDeserialize) { + pNext = nullptr; + fold_helper::deserialize(pDeserialize, LightInfoDiskVars); +} +void LightInfoDisk::_dtor() { +} + + +#define LightInfoCylinderVars sType, \ + position, \ + radius, \ + axis, \ + axisLength +uint32_t LightInfoCylinder::_calcSize() const { + return fold_helper::calcSize(LightInfoCylinderVars); +} +void LightInfoCylinder::_serialize(void*& pSerialize) const { + fold_helper::serialize(pSerialize, LightInfoCylinderVars); +} +void LightInfoCylinder::_deserialize(void*& pDeserialize) { + pNext = nullptr; + fold_helper::deserialize(pDeserialize, LightInfoCylinderVars); +} +void LightInfoCylinder::_dtor() { +} + + +#define LightInfoDistantVars sType, \ + direction, \ + angularDiameterDegrees +uint32_t LightInfoDistant::_calcSize() const { + return fold_helper::calcSize(LightInfoDistantVars); +} +void LightInfoDistant::_serialize(void*& pSerialize) const { + fold_helper::serialize(pSerialize, LightInfoDistantVars); +} +void LightInfoDistant::_deserialize(void*& pDeserialize) { + pNext = nullptr; + fold_helper::deserialize(pDeserialize, LightInfoDistantVars); +} +void LightInfoDistant::_dtor() { +} + + + +#define LightInfoDomeVars sType, \ + transform, \ + colorTexture +uint32_t LightInfoDome::_calcSize() const { + return fold_helper::calcSize(LightInfoDomeVars); +} +void LightInfoDome::_serialize(void*& pSerialize) const { + fold_helper::serialize(pSerialize, LightInfoDomeVars); +} +void LightInfoDome::_deserialize(void*& pDeserialize) { + pNext = nullptr; + fold_helper::deserialize(pDeserialize, LightInfoDomeVars); +} +void LightInfoDome::_dtor() { + delete colorTexture; +} + + +// remixapi_LightInfoUSDEXT uses a pattern wherein optional members +// are determined by whether a given pointer is NULL or not, therefore +// we have defined some helpers in an anonymous namespace to operate on +// such members a bit differently, and have separated the variables +#define LightInfoUSDVars sType, \ + lightType, \ + transform +#define LightInfoUSDOptionalVars pRadius, \ + pWidth, \ + pHeight, \ + pLength, \ + pAngleRadians, \ + pEnableColorTemp, \ + pColor, \ + pColorTemp, \ + pExposure, \ + pIntensity, \ + pConeAngleRadians, \ + pConeSoftness, \ + pFocus +// calcSize helpers +namespace{ +template +static inline uint32_t sizeOf__optionalPointer(const T* pObj) { + uint32_t size = bridge_util::sizeOf(); // We serialize out bools to indicated populated value + if(pObj != nullptr) { + size += bridge_util::sizeOf>(); + } + return size; +} +template +static uint32_t calcSize__optionalPointer(const Types&... args) { + return (sizeOf__optionalPointer(args) + ...); +} +} +uint32_t LightInfoUSD::_calcSize() const { + return fold_helper::calcSize(LightInfoUSDVars) + + calcSize__optionalPointer(LightInfoUSDOptionalVars); +} +// serialize helpers +namespace{ +template +static inline void serialize__optionalPointer(const T* pObj, void*& pSerialize) { + const bool bHasValue = (pObj != nullptr); + bridge_util::serialize(bHasValue, pSerialize); + if(bHasValue) { + bridge_util::serialize(*pObj, pSerialize); + } +} +template +static void fold_serialize__optionalPointer(void*& pSerialize, const Types&... args) { + return (serialize__optionalPointer(args, pSerialize) , ...); +} +} +void LightInfoUSD::_serialize(void*& pSerialize) const { + fold_helper::serialize(pSerialize, LightInfoUSDVars); + fold_serialize__optionalPointer(pSerialize, LightInfoUSDOptionalVars); +} +// deserialize helpers +namespace{ +template +static inline void deserialize__optionalPointer(void*& pDeserialize, T& val) { + bool bHasValue = false; + bridge_util::deserialize(pDeserialize, bHasValue); + if(bHasValue) { + deserialize_const_p(pDeserialize, val, bridge_util::sizeOf(*val)); + } else { + val = nullptr; + } +} +template +static void fold_deserialize__optionalPointer(void*& pDeserialize, Types&... args) { + return (deserialize__optionalPointer(pDeserialize, args) , ...); +} +} +void LightInfoUSD::_deserialize(void*& pDeserialize) { + pNext = nullptr; + fold_helper::deserialize(pDeserialize, LightInfoUSDVars); + fold_deserialize__optionalPointer(pDeserialize, LightInfoUSDOptionalVars); +} +void LightInfoUSD::_dtor() { + if (pRadius) delete pRadius; + if (pWidth) delete pWidth; + if (pHeight) delete pHeight; + if (pLength) delete pLength; + if (pAngleRadians) delete pAngleRadians; + if (pEnableColorTemp) delete pEnableColorTemp; + if (pColor) delete pColor; + if (pColorTemp) delete pColorTemp; + if (pExposure) delete pExposure; + if (pIntensity) delete pIntensity; + if (pConeAngleRadians) delete pConeAngleRadians; + if (pConeSoftness) delete pConeSoftness; + if (pFocus) delete pFocus; +} + +} \ No newline at end of file diff --git a/src/util/util_remixapi.h b/src/util/util_remixapi.h new file mode 100644 index 0000000..8472dd3 --- /dev/null +++ b/src/util/util_remixapi.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ +#pragma once + +#include "util_common.h" +#include "util_serializable.h" + +#include + +#include + +#define ASSERT_REMIXAPI_PFN_TYPE(REMIXAPI_FN_NAME) static_assert(std::is_same_v< decltype(&REMIXAPI_FN_NAME), PFN_##REMIXAPI_FN_NAME >) + +namespace remixapi { + +namespace util { + +struct HandleUID { + const uint32_t uid = 0; + HandleUID(const HandleUID& handle) = default; + HandleUID(HandleUID&& handle) = default; +#ifdef REMIX_BRIDGE_CLIENT + static inline uint32_t nextUid = 1; + HandleUID() : uid(nextUid++) {} +#endif + template + HandleUID(const T* const p) : uid((uint32_t)(uintptr_t)(p)) { + static_assert(std::is_pointer_v); + } + template + operator T*() { + static_assert(std::is_pointer_v); + return reinterpret_cast((uintptr_t)uid); + } +#ifdef REMIX_BRIDGE_SERVER + HandleUID(const uint32_t val) : uid(val) {} + operator uint32_t () { + return uid; + } +#endif + bool isValid() const { +#ifdef REMIX_BRIDGE_CLIENT + return uid > 0 && uid < nextUid; +#endif +#ifdef REMIX_BRIDGE_SERVER + return uid > 0; +#endif + } +}; +static_assert(sizeof(HandleUID::uid) == sizeof(HandleUID)); + +struct AnyInfoPrototype { + remixapi_StructType sType; + void* pNext; +}; + +static inline remixapi_StructType getSType(const void* const pInfo) { + if (pInfo) { + return reinterpret_cast(pInfo)->sType; + } + return REMIXAPI_STRUCT_TYPE_NONE; +} + +static inline void* getPNext(const void* const pInfo) { + if (pInfo) { + return reinterpret_cast(pInfo)->pNext; + } + return nullptr; +} + +template +static inline AnyInfoPrototype& getInfoProto(RemixApiT& remixApiT) { + return reinterpret_cast(remixApiT); +} + +template< typename T > constexpr remixapi_StructType ToRemixApiStructEnum = REMIXAPI_STRUCT_TYPE_NONE; +template<> constexpr auto ToRemixApiStructEnum< remixapi_MaterialInfo > = REMIXAPI_STRUCT_TYPE_MATERIAL_INFO; +template<> constexpr auto ToRemixApiStructEnum< remixapi_MaterialInfoPortalEXT > = REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_PORTAL_EXT; +template<> constexpr auto ToRemixApiStructEnum< remixapi_MaterialInfoTranslucentEXT > = REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_TRANSLUCENT_EXT; +template<> constexpr auto ToRemixApiStructEnum< remixapi_MaterialInfoOpaqueEXT > = REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_OPAQUE_EXT; +template<> constexpr auto ToRemixApiStructEnum< remixapi_MaterialInfoOpaqueSubsurfaceEXT> = REMIXAPI_STRUCT_TYPE_MATERIAL_INFO_OPAQUE_SUBSURFACE_EXT; +template<> constexpr auto ToRemixApiStructEnum< remixapi_LightInfoSphereEXT > = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_SPHERE_EXT; +template<> constexpr auto ToRemixApiStructEnum< remixapi_LightInfoRectEXT > = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_RECT_EXT; +template<> constexpr auto ToRemixApiStructEnum< remixapi_LightInfoDiskEXT > = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DISK_EXT; +template<> constexpr auto ToRemixApiStructEnum< remixapi_LightInfoCylinderEXT > = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_CYLINDER_EXT; +template<> constexpr auto ToRemixApiStructEnum< remixapi_LightInfoDistantEXT > = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DISTANT_EXT; +template<> constexpr auto ToRemixApiStructEnum< remixapi_LightInfoDomeEXT > = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_DOME_EXT; +template<> constexpr auto ToRemixApiStructEnum< remixapi_LightInfoUSDEXT > = REMIXAPI_STRUCT_TYPE_LIGHT_INFO_USD_EXT; +template<> constexpr auto ToRemixApiStructEnum< remixapi_LightInfo > = REMIXAPI_STRUCT_TYPE_LIGHT_INFO; +template<> constexpr auto ToRemixApiStructEnum< remixapi_MeshInfo > = REMIXAPI_STRUCT_TYPE_MESH_INFO; +template<> constexpr auto ToRemixApiStructEnum< remixapi_InstanceInfo > = REMIXAPI_STRUCT_TYPE_INSTANCE_INFO; +template<> constexpr auto ToRemixApiStructEnum< remixapi_InstanceInfoBoneTransformsEXT > = REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_BONE_TRANSFORMS_EXT; +template<> constexpr auto ToRemixApiStructEnum< remixapi_InstanceInfoBlendEXT > = REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_BLEND_EXT; +template<> constexpr auto ToRemixApiStructEnum< remixapi_InstanceInfoObjectPickingEXT > = REMIXAPI_STRUCT_TYPE_INSTANCE_INFO_OBJECT_PICKING_EXT; +template<> constexpr auto ToRemixApiStructEnum< remixapi_CameraInfo > = REMIXAPI_STRUCT_TYPE_CAMERA_INFO; +template<> constexpr auto ToRemixApiStructEnum< remixapi_CameraInfoParameterizedEXT > = REMIXAPI_STRUCT_TYPE_CAMERA_INFO_PARAMETERIZED_EXT; + + +namespace serialize { +// Type declaration + +// MaterialInfo +using MaterialInfo = bridge_util::Serializable; +using MaterialInfoOpaque = bridge_util::Serializable; +using MaterialInfoOpaqueSubsurface = bridge_util::Serializable; +using MaterialInfoTranslucent = bridge_util::Serializable; +using MaterialInfoPortal = bridge_util::Serializable; + +// MeshInfo +using MeshInfo = bridge_util::Serializable; + +// InstanceInfo +using InstanceInfo = bridge_util::Serializable; +using InstanceInfoObjectPicking = bridge_util::Serializable; +using InstanceInfoBlend = bridge_util::Serializable; +using InstanceInfoTransforms = bridge_util::Serializable; + +// Light Info +using LightInfo = bridge_util::Serializable; +using LightInfoSphere = bridge_util::Serializable; +using LightInfoRect = bridge_util::Serializable; +using LightInfoDisk = bridge_util::Serializable; +using LightInfoCylinder = bridge_util::Serializable; +using LightInfoDistant = bridge_util::Serializable; +using LightInfoDome = bridge_util::Serializable; +using LightInfoUSD = bridge_util::Serializable; + +} + +} + +} diff --git a/src/util/util_serializable.h b/src/util/util_serializable.h new file mode 100644 index 0000000..5375dcf --- /dev/null +++ b/src/util/util_serializable.h @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ +#pragma once + +#include +#include +#include + +namespace bridge_util { + +template +class Serializable : public T { +public: + using BaseT = T; + static inline constexpr bool bHasStaticSize = HasStaticSize; + + Serializable() {} // Allow non-functional placeholder Serializables + Serializable(const Serializable& other) = delete; // Deep copy would be necessary, probably overkill + Serializable(Serializable&& other) { + *this = std::move(other); + } + + // Serializing ctor + Serializable(const BaseT& serializeMe) + : kType(Serialize) + , BaseT(serializeMe) + , m_kSize(initSize()) {} + // Deserializing ctor + Serializable(void* pDeserializeMe) + : kType(Deserialize) + , m_pDeserializeMe(pDeserializeMe) + , m_kSize(initSize(pDeserializeMe)) {} + + ~Serializable() { + // Static sized struct implies POD with no variable length pointers. + // Trivial implicit dtor sufficient + if constexpr (!bHasStaticSize) { + // User code that serializes a given struct is in charge of freeing relevant memory + if(kType == Deserialize) { + _dtor(); + } + } + } + + Serializable& operator=(Serializable&& other) { + Serializable::BaseT::operator=(other); + kType = other.kType; + m_kSize = other.m_kSize; + m_pDeserializeMe = other.m_pDeserializeMe; + other.kType = Invalid; + other.m_kSize = 0; + other.m_pDeserializeMe = nullptr; + memset(static_cast(&other), 0x0, sizeof(BaseT)); + return *this; + } + + inline uint32_t size() const { + if constexpr (bHasStaticSize) { + return s_kSize; + } else { + return m_kSize; + } + }; + uint32_t calcSize() const { + return _calcSize() + sizeof(uint32_t); // calculated size of type + size storage + } + void serialize(void* const pSerializeBegin) const { + assert(kType == Serialize && "[serialize] Coding error: This serializable type was constructed for deserializing!"); + const auto startPos = reinterpret_cast(pSerializeBegin); + void* pSerialize = pSerializeBegin; + bridge_util::serialize(size(), pSerialize); + _serialize(pSerialize); + const auto endPos = reinterpret_cast(pSerialize); + assert((endPos - startPos) == size()); + } + void deserialize() { + assert(kType == Deserialize && "[deserialize] Coding error: This serializable type was constructed for serializing!"); + auto startPos = reinterpret_cast(m_pDeserializeMe); + void* pDeserialize = m_pDeserializeMe; + uint32_t deserializedSize = 0; + bridge_util::deserialize(pDeserialize, deserializedSize); + assert(deserializedSize == size()); + _deserialize(pDeserialize); + const auto endPos = reinterpret_cast(pDeserialize); + assert((endPos - startPos) == size()); + } + +private: + uint32_t _calcSize() const; // Implement me + void _serialize(void*& pSerialize) const; // Implement me + void _deserialize(void*& pDeserialize); // Implement me + void _dtor(); // Implement me + + uint32_t initSize(const void* deserializeMe = nullptr) const { + if constexpr (bHasStaticSize) { + return s_kSize; + } else { + if(deserializeMe) { + return *(const uint32_t*)deserializeMe; + } else { + return calcSize(); + } + } + } + static inline uint32_t initStaticSize() { + if constexpr (bHasStaticSize) { + return calcStaticSize(); + } else { + return 0; + } + } + static uint32_t calcStaticSize() { + static_assert(bHasStaticSize); + BaseT dummyA; + Serializable dummyB(dummyA); + return dummyB.calcSize(); + } + + enum Type { + Invalid, + Serialize, + Deserialize + } kType = Invalid; + void* m_pDeserializeMe = nullptr; + uint32_t m_kSize = 0; +public: + static inline const uint32_t s_kSize = initStaticSize(); +}; + +template< typename T > +using underlying = std::remove_cv_t< std::remove_pointer_t< std::remove_reference_t< std::remove_cv_t< std::remove_all_extents_t< T > > > > >; +// Double std::remove_cv_t necessary because prior to removing the pointer, +// std::remove_cv_t only affects ptr constness + +// Would just use is_arithmetic, but need to include enums. +// Would use is_scalar, but don't want to include pointers. +template +struct is_default_sizeOf_allowed : std::integral_constant> || std::is_enum_v> >{}; +template +inline constexpr bool is_default_sizeOf_allowed_v = is_default_sizeOf_allowed::value; + + +// sizeOf(...) templates +// Convenience implementations for simple types +// e.g. integral, floats, enums, arrays thereof +template +static inline constexpr uint32_t sizeOf() { + static_assert(bridge_util::is_default_sizeOf_allowed_v, "Default constexpr variant of \"sizeOf()\" can only be used for integral, floating point, and enum types."); + return sizeof(T); +} +template +static inline uint32_t sizeOf(const T& obj) { + return sizeOf(); +} + +// serialize(...) +// The core serializing function. memcpys from one ptr to another, and increments the +// pointer *being copied to* by size copied. +static void serialize(const void* const serializeFrom, void*& serializeTo, const uint32_t size) { + std::memcpy(serializeTo, serializeFrom, size); + (reinterpret_cast(serializeTo)) += size; +} + +// deserialize(...) +// The core deserializing function. memcpys from one ptr to another, and increments the +// pointer *being copied from* by size copied. +static void deserialize(void*& deserializeFrom, void* const deserializeTo, const uint32_t size) { + std::memcpy(deserializeTo, deserializeFrom, size); + (reinterpret_cast(deserializeFrom)) += size; +} + +// Convenience templated function for serializing simply laid-out types +// with defined sizeOf() +template +void serialize(const T& serializeFrom, void*& serializeTo) { + static_assert(!std::is_pointer_v, "Implement a specialization for the pointer type in question OR pass in the type pointed to."); + serialize(&serializeFrom, serializeTo, sizeOf(serializeFrom)); +} +// Convenience templated function for deserializing simply laid-out types +// with defined sizeOf() +template +void deserialize(void*& deserializeFrom, T& deserializeTo) { + static_assert(!std::is_const_v, "Cannot deserialize to a const type. Implement a specialization or correct the struct being deserialized."); + static_assert(!std::is_pointer_v, "Implement a specialization for the pointer type in question OR pass in the type pointed to."); + deserialize(deserializeFrom, &deserializeTo, sizeOf(deserializeTo)); +} +// NOTE: Ensure that non-integral/-float types are not casually de-/serialized +// sans an explicit size parameter UNLESS a user-defined sizeOf implementation +// exists. `sizeOf()` implementation should fire a static_assert accordingly. + + +// Size of bool is compiler implementation specific, so we should +// ensure that it is locked to a given size across architectures +enum class Bool : uint8_t { + False = 0, + True = 0xff +}; +template<> +static inline constexpr uint32_t sizeOf() { + return sizeof(Bool); +} +template<> +static inline void serialize(const bool& serializeFrom, void*& serializeTo) { + const Bool b = (serializeFrom) ? Bool::True : Bool::False; + serialize(&b, serializeTo, sizeOf()); +} +template<> +static inline void deserialize(void*& deserializeFrom, bool& deserializeTo) { + Bool b = Bool::True; + deserialize(deserializeFrom, &b, sizeOf()); + deserializeTo = (b == Bool::True); +} + + +// https://stackoverflow.com/a/31763111 +template +struct is_serializable // Default case, no pattern match + : std::false_type {}; +template +struct is_serializable< bridge_util::Serializable > // For types matching the pattern A + : std::true_type {}; +template +inline constexpr bool is_serializable_v = is_serializable::value; + +// Use below helpers to quickly define Serializables that only have +// members whose sizes are determined trivially. For example, if +// a struct's member points to a variable-sized blob of memory, then +// you should not use these and must define calcSize(), serialize(), +// and deserialize() explicitly. +// See util_remixapi.h/.cpp for examples +namespace fold_helper { + +template +static uint32_t calcSize(const Types&... args) { + return (bridge_util::sizeOf(args) + ...); +} + +template +static void serialize(void*& pSerialize, const Types&... args) { + (bridge_util::serialize(args, pSerialize) , ...); +} + +template +static void deserialize(void*& pDeserialize, Types&... args) { + (bridge_util::deserialize(pDeserialize, args), ...); +} + +} + +}