File: dx12_dll_initializer.h

package info (click to toggle)
gfxreconstruct 0.9.18%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 24,636 kB
  • sloc: cpp: 328,961; ansic: 25,454; python: 18,156; xml: 255; sh: 128; makefile: 6
file content (319 lines) | stat: -rw-r--r-- 11,473 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
/*
** Copyright (c) 2021 LunarG, Inc.
**
** 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 GFXRECON_ENCODE_DX12_DLL_INITIALIZER_H
#define GFXRECON_ENCODE_DX12_DLL_INITIALIZER_H

#include "util/defines.h"
#include "util/platform.h"
#include "util/file_path.h"

#include <string>
#include <windows.h>
#include <Shlobj.h>

GFXRECON_BEGIN_NAMESPACE(gfxrecon)
GFXRECON_BEGIN_NAMESPACE(encode)

const char kDx12RedistRuntime[] = "D3D12Core.dll";

bool FoundRenamedCaptureModule(const std::string& renamed_module)
{
    char        module_name[MAX_PATH] = {};
    int         path_size             = GetModuleFileNameA(nullptr, module_name, MAX_PATH);
    size_t      separator_pos         = std::string(module_name).find_last_of(util::filepath::kPathSep);
    std::string exe_root              = std::string(module_name).substr(0, separator_pos);
    std::string legacy_path           = exe_root + util::filepath::kPathSep + renamed_module;

    return util::filepath::Exists(legacy_path);
}

std::string GetWindowsModuleRoot()
{
    std::string windows_module_root = "";

    // Get actual Windows folder
    char     win_dir[MAX_PATH]  = {};
    uint32_t windows_dir_result = GetWindowsDirectory(win_dir, MAX_PATH);

    if (windows_dir_result != 0)
    {
        windows_module_root = std::string(win_dir) + util::filepath::kPathSep + std::string("System32");

        // Check if app is 32-bit
        BOOL process_32_bit = FALSE;
        BOOL found_bitness  = IsWow64Process(GetCurrentProcess(), &process_32_bit);

        if (found_bitness != 0)
        {
            if (process_32_bit == TRUE)
            {
                windows_module_root = std::string(win_dir) + util::filepath::kPathSep + std::string("SysWOW64");
            }
        }
    }

    return windows_module_root;
}

bool CopyModule(const std::string& src, const std::string& dst)
{
    // Copy the runtime to "\AppData\Roaming\GFXReconstruct\dx-runtimes"
    BOOL copy_result = CopyFile(src.c_str(), dst.c_str(), FALSE);

    // Only update module path if we're good until now
    if (copy_result == 0)
    {
        std::string debug_str = std::string("GFXRECON: Failed to copy ") + src;
        OutputDebugStringA(debug_str.c_str());
    }

    return copy_result != 0;
}

bool UpdateModule(const std::string& src, const std::string& dst)
{
    bool module_up_to_date = false;

    if (util::filepath::Exists(dst) == true)
    {
        if (util::filepath::FilesEqual(src, dst) == true)
        {
            module_up_to_date = true;
        }
        else
        {
            module_up_to_date = CopyModule(src, dst);
        }
    }
    else
    {
        module_up_to_date = CopyModule(src, dst);
    }

    return module_up_to_date;
}

std::string PrepGfxrRuntimesFolder(const std::string& windows_module_root)
{
    std::string gfxr_runtimes_root = "";

    // Get folder location for "\AppData\Roaming"
    char roaming_path[MAX_PATH] = {};
    if (SUCCEEDED(SHGetFolderPath(nullptr, CSIDL_APPDATA, nullptr, 0, roaming_path)))
    {
        std::string gfxr_user_target = std::string(roaming_path) + util::filepath::kPathSep + std::string("GFXReconstruct");
        std::string gfxr_runtimes_target = gfxr_user_target + util::filepath::kPathSep + std::string("dx-runtimes");

        // If "\AppData\Roaming\GFXReconstruct\dx-runtimes" does not exist, create it
        if (util::filepath::Exists(gfxr_runtimes_target) == false)
        {
            int32_t mkdir_result = 0;

            // If "\AppData\Roaming\GFXReconstruct" does not exist, create it
            if (util::filepath::Exists(gfxr_user_target) == false)
            {
                mkdir_result = gfxrecon::util::platform::MakeDirectory(gfxr_user_target.c_str());
            }

            // Create "\AppData\Roaming\GFXReconstruct\dx-runtimes"
            if (mkdir_result == 0)
            {
                mkdir_result = gfxrecon::util::platform::MakeDirectory(gfxr_runtimes_target.c_str());
            }

            if (mkdir_result == 0)
            {
                gfxr_runtimes_root = gfxr_runtimes_target;
            }
            else
            {
                std::string debug_str = std::string("GFXRECON: Failed to setup GFXR user folder");
                OutputDebugStringA(debug_str.c_str());
            }
        }
        else
        {
            gfxr_runtimes_root = gfxr_runtimes_target;
        }

        // Once created "\AppData\Roaming\GFXReconstruct\dx-runtimes" copy in the current in-box D3D12 runtime
        if (gfxr_runtimes_root.empty() == false)
        {
            std::string redist_runtime_path_src =
                windows_module_root + util::filepath::kPathSep + std::string(kDx12RedistRuntime);
            std::string redist_runtime_path_dst =
                gfxr_runtimes_root + util::filepath::kPathSep + std::string(kDx12RedistRuntime);

            UpdateModule(redist_runtime_path_src, redist_runtime_path_dst);
        }
    }

    return gfxr_runtimes_root;
}

std::string SetupCaptureModule(const std::string& dll_name, const std::string& dll_name_renamed)
{
    // First assume we are starting with the "legacy" method with renamed capture binaries
    std::string module_path = dll_name_renamed;

    // If the renamed DLLs are not found beside the app executable, then setup for reading from "\AppData\Roaming"
    if (FoundRenamedCaptureModule(dll_name_renamed) == false)
    {
        std::string windows_module_root = GetWindowsModuleRoot();

        // If obtained path to Windows (for correct app bitness)
        if (windows_module_root.empty() == false)
        {
            // Make sure the roaming folder is present and ready
            std::string gfxr_user_roaming_root = PrepGfxrRuntimesFolder(windows_module_root);

            if (gfxr_user_roaming_root.empty() == false)
            {
                std::string module_path_src = windows_module_root + util::filepath::kPathSep + dll_name;
                std::string module_path_dst = gfxr_user_roaming_root + util::filepath::kPathSep + dll_name_renamed;

                if (UpdateModule(module_path_src, module_path_dst) == true)
                {
                    module_path = module_path_dst;
                }
            }
        }
    }

    return module_path;
}

template <typename DispatchTableT>
class DxDllInitializer
{
  public:
    bool Initialize(const char* system_dll_name,
                    const char* capture_dll_name,
                    const char* initialize_func_name,
                    void (*LoadCaptureProcs)(HMODULE, DispatchTableT*))
    {
        if (util::interception::UseDetoursHooking() == false)
        {
            if (system_dll_ == nullptr)
            {
                system_dll_ = util::platform::OpenLibrary(system_dll_name);

                if (system_dll_ != nullptr)
                {
                    LoadCaptureProcs(system_dll_, &dispatch_table_);

                    if (IsCaptureEnabled() && (capture_dll_ == nullptr))
                    {
                        capture_dll_ = util::platform::OpenLibrary(capture_dll_name);
                        if (capture_dll_ != nullptr)
                        {
                            auto init_func =
                                reinterpret_cast<InitializeFuncT>(GetProcAddress(capture_dll_, initialize_func_name));

                            if (init_func != nullptr)
                            {
                                init_func(&dispatch_table_);
                            }
                            else
                            {
                                std::string debug_str =
                                    std::string("GFXRECON: Failed to retrieve '") + initialize_func_name +
                                    "' proc from GFXReconstruct capture DLL '" + capture_dll_name + "'";
                                // TODO: Unify logging
                                OutputDebugStringA(debug_str.c_str());
                                OutputDebugStringA("GFXRECON: GFXReconstruct capture will be disabled");
                            }
                        }
                        else
                        {
                            std::string debug_str =
                                std::string("GFXRECON: Failed to load GFXReconstruct capture DLL '") +
                                capture_dll_name + "' needed to get proc '" + initialize_func_name + "'";
                            // TODO: Unify logging
                            OutputDebugStringA(debug_str.c_str());
                            OutputDebugStringA("GFXRECON: GFXReconstruct capture will be disabled");
                        }
                    }
                }
                else
                {
                    std::string debug_str =
                        std::string("GFXRECON: Failed to load system DLL '") + system_dll_name + "'";
                    // TODO: Unify logging
                    OutputDebugStringA(debug_str.c_str());
                    return false;
                }
            }
        }

        return true;
    }

    void Destroy(const char* release_func_name)
    {
        if (capture_dll_ != nullptr)
        {
            auto release_func = reinterpret_cast<ReleaseFuncT>(GetProcAddress(capture_dll_, release_func_name));

            if (release_func != nullptr)
            {
                release_func(&dispatch_table_);
            }

            util::platform::CloseLibrary(capture_dll_);
            capture_dll_ = nullptr;
        }

        if (system_dll_ != nullptr)
        {
            util::platform::CloseLibrary(system_dll_);
            system_dll_ = nullptr;
        }
    }

    static bool IsCaptureEnabled()
    {
        // TODO: Read environment variable.
        return true;
    }

    const DispatchTableT& GetDispatchTable() const { return dispatch_table_; }

  private:
    typedef bool (*InitializeFuncT)(DispatchTableT*);
    typedef void (*ReleaseFuncT)(DispatchTableT*);

  private:
    DispatchTableT dispatch_table_;

    // System DLL providing the DX API calls to wrap.
    util::platform::LibraryHandle system_dll_{ nullptr };

    // DLL with capture implementation, which is only loaded when capture is enabled.
    util::platform::LibraryHandle capture_dll_{ nullptr };
};

GFXRECON_END_NAMESPACE(encode)
GFXRECON_END_NAMESPACE(gfxrecon)

#endif // GFXRECON_ENCODE_DX12_DLL_INITIALIZER_H