yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Gangzheng TongAdd RHI Device Caching and Test Prefix Exclusion (#8448)ba8132345

master
4.5 KiB160 linesraw
1#include "slang-test-device-cache.h"
2
3#include <algorithm>
4
5// Static member accessor functions (Meyer's singleton pattern)
6// This ensures proper destruction order - function-local statics are destroyed
7// in reverse order of first access, avoiding the static destruction order fiasco
8std::mutex& DeviceCache::getMutex()
9{
10    static std::mutex instance;
11    return instance;
12}
13
14std::unordered_map<
15    DeviceCache::DeviceCacheKey,
16    DeviceCache::CachedDevice,
17    DeviceCache::DeviceCacheKeyHash>&
18DeviceCache::getDeviceCache()
19{
20    static std::unordered_map<DeviceCacheKey, CachedDevice, DeviceCacheKeyHash> instance;
21    return instance;
22}
23
24uint64_t& DeviceCache::getNextCreationOrder()
25{
26    static uint64_t instance = 0;
27    return instance;
28}
29
30bool DeviceCache::DeviceCacheKey::operator==(const DeviceCacheKey& other) const
31{
32    return deviceType == other.deviceType && enableValidation == other.enableValidation &&
33           enableRayTracingValidation == other.enableRayTracingValidation &&
34           profileName == other.profileName && requiredFeatures == other.requiredFeatures;
35}
36
37std::size_t DeviceCache::DeviceCacheKeyHash::operator()(const DeviceCacheKey& key) const
38{
39    std::size_t h1 = std::hash<int>{}(static_cast<int>(key.deviceType));
40    std::size_t h2 = std::hash<bool>{}(key.enableValidation);
41    std::size_t h3 = std::hash<bool>{}(key.enableRayTracingValidation);
42    std::size_t h4 = std::hash<std::string>{}(key.profileName);
43
44    std::size_t h5 = 0;
45    for (const auto& feature : key.requiredFeatures)
46    {
47        h5 ^= std::hash<std::string>{}(feature) + 0x9e3779b9 + (h5 << 6) + (h5 >> 2);
48    }
49
50    return h1 ^ (h2 << 1) ^ (h3 << 2) ^ (h4 << 3) ^ (h5 << 4);
51}
52
53DeviceCache::CachedDevice::CachedDevice()
54    : creationOrder(0)
55{
56}
57
58void DeviceCache::evictOldestDeviceIfNeeded()
59{
60    auto& deviceCache = getDeviceCache();
61    if (deviceCache.size() < MAX_CACHED_DEVICES)
62        return;
63
64    // Find the oldest device to evict
65    auto oldestIt = deviceCache.end();
66    uint64_t oldestCreationOrder = UINT64_MAX;
67
68    for (auto it = deviceCache.begin(); it != deviceCache.end(); ++it)
69    {
70        if (it->second.creationOrder < oldestCreationOrder)
71        {
72            oldestCreationOrder = it->second.creationOrder;
73            oldestIt = it;
74        }
75    }
76
77    // Remove the oldest device - ComPtr will handle the actual device release
78    if (oldestIt != deviceCache.end())
79    {
80        deviceCache.erase(oldestIt);
81    }
82}
83
84SlangResult DeviceCache::acquireDevice(const rhi::DeviceDesc& desc, rhi::IDevice** outDevice)
85{
86    if (!outDevice)
87        return SLANG_E_INVALID_ARG;
88
89    *outDevice = nullptr;
90
91    // Skip caching for CUDA devices due to crashes
92    if (desc.deviceType == rhi::DeviceType::CUDA)
93    {
94        return rhi::getRHI()->createDevice(desc, outDevice);
95    }
96
97    std::lock_guard<std::mutex> lock(getMutex());
98    auto& deviceCache = getDeviceCache();
99    auto& nextCreationOrder = getNextCreationOrder();
100
101    // Create cache key
102    DeviceCacheKey key;
103    key.deviceType = desc.deviceType;
104    key.enableValidation = desc.enableValidation;
105    key.enableRayTracingValidation = desc.enableRayTracingValidation;
106    key.profileName = desc.slang.targetProfile ? desc.slang.targetProfile : "Unknown";
107
108    // Add required features to key
109    for (int i = 0; i < desc.requiredFeatureCount; ++i)
110    {
111        key.requiredFeatures.push_back(desc.requiredFeatures[i]);
112    }
113    std::sort(key.requiredFeatures.begin(), key.requiredFeatures.end());
114
115    // Evict oldest device if we've reached the limit
116    evictOldestDeviceIfNeeded();
117
118    // Check if we have a cached device
119    auto it = deviceCache.find(key);
120    if (it != deviceCache.end())
121    {
122        // Return the cached device - COM reference counting handles the references
123        *outDevice = it->second.device.get();
124        if (*outDevice)
125        {
126            (*outDevice)->addRef();
127            return SLANG_OK;
128        }
129    }
130
131    // Create new device
132    Slang::ComPtr<rhi::IDevice> device;
133    auto result = rhi::getRHI()->createDevice(desc, device.writeRef());
134    if (SLANG_FAILED(result))
135    {
136        return result;
137    }
138
139    // Cache the device
140    CachedDevice& cached = deviceCache[key];
141    cached.device = device;
142    cached.creationOrder = nextCreationOrder++;
143
144    // Return the device with proper reference counting
145    *outDevice = device.get();
146    if (*outDevice)
147    {
148        (*outDevice)->addRef();
149    }
150
151    return SLANG_OK;
152}
153
154
155void DeviceCache::cleanCache()
156{
157    std::lock_guard<std::mutex> lock(getMutex());
158    auto& deviceCache = getDeviceCache();
159    deviceCache.clear();
160}