Program Listing for File deviceRater.cpp

Return to documentation for file (PrismEngine/src/deviceRater.cpp)

#include <unordered_set>
#include <algorithm>
#include "deviceRater.h"
#include "base.h"
#include "deviceWrapper.h"
#include "deviceChecker.h"

int prism::PGC::L2::DeviceRater::rate(VkPhysicalDevice device)
{
    debugDeviceSelection = settings->debug.debugDeviceSelection;
    // Базовые проверки
    DeviceChecker deviceChecker(context, settings);
    if (!deviceChecker.check(device)) return 0;

    DeviceScore deviceScore;
    deviceScore.typeScore     = (int)(getDeviceTypeScore(device)     * settings->deviceEvaluationWeights.wType);
    deviceScore.featureScore  = (int)(getDeviceFeatureScore(device)  * settings->deviceEvaluationWeights.wFeatures);
    deviceScore.hardwareScore = (int)(getDeviceHardwareScore(device) * settings->deviceEvaluationWeights.wHardware);
    deviceScore.apiScore      = (int)(getDeviceApiScore(device)      * settings->deviceEvaluationWeights.wApi);

    if (debugDeviceSelection) {
       ScoreWrapper::print(deviceScore);
    }


    return ScoreWrapper::getTotal(deviceScore);
}

int prism::PGC::L2::DeviceRater::getDeviceTypeScore(VkPhysicalDevice device)
{
    switch (prism::PGC::L3::DeviceWrapper::getDeviceProperties(device).deviceType) {
    case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:   return 100;
    case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: return 70;
    case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:    return 40;
    case VK_PHYSICAL_DEVICE_TYPE_CPU:            return 10;
    default:                                     return 10;
    }
}

int prism::PGC::L2::DeviceRater::getDeviceFeatureScore(VkPhysicalDevice device)
{
    FeatureScores features = calculateFeatureScore(device);

    // Учитываем вендора для RT и Upscaling
    VkPhysicalDeviceProperties props = L3::DeviceWrapper::getDeviceProperties(device);;

    if (props.vendorID == 0x10DE) { // NVIDIA
        features.raytracing *= 1.5f;
        if (features.upscaling > 0) features.upscaling += 5.0f;
    }

    if (debugDeviceSelection) {
        ScoreWrapper::print(features);
    }

    return static_cast<int>(ScoreWrapper::getTotal(features));
}

int prism::PGC::L2::DeviceRater::getDeviceHardwareScore(VkPhysicalDevice device)
{
    HardwareScore score;
    VkPhysicalDeviceProperties props = L3::DeviceWrapper::getDeviceProperties(device);
    VkPhysicalDeviceMemoryProperties memProps = L3::DeviceWrapper::getDeviceMemoryProperties(device);

    // Константы для преобразования единиц
    constexpr float BYTES_TO_GB = 1.0f / (1024 * 1024 * 1024);
    constexpr float MHZ_TO_GHZ = 1.0f / 1000.0f;
    constexpr float TEXTURE_UNITS_NORMALIZER = 1.0f / 10000.0f;

    // Расчет VRAM
    for (uint32_t i = 0; i < memProps.memoryHeapCount; ++i) {
        if (memProps.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) {
            score.vramGB += static_cast<float>(memProps.memoryHeaps[i].size) * BYTES_TO_GB;
        }
    }

    // показатели производительности
    score.shaderCores = std::log2(static_cast<float>(props.limits.maxComputeWorkGroupInvocations) + 1.0f);

    if (props.limits.maxComputeWorkGroupSize[0] > 0) {
        score.clockSpeedGHz = static_cast<float>(props.limits.maxComputeWorkGroupSize[0]) * MHZ_TO_GHZ;
    }
    else {
        score.clockSpeedGHz = 1.0f;
    }

    score.textureUnits = static_cast<float>(props.limits.maxImageDimension2D) * TEXTURE_UNITS_NORMALIZER;

    if (debugDeviceSelection) {
        ScoreWrapper::print(score);
    }

    return ScoreWrapper::getTotal(score);
}

int prism::PGC::L2::DeviceRater::getDeviceApiScore(VkPhysicalDevice device)
{
    const int BASE_SCORE_V1 = 30;
    const int V1_1_BONUS = 5;
    const int V1_2_BONUS = 15;
    const int V1_3_BONUS = 25;
    const int FEATURE_BONUS = 5;

    VkPhysicalDeviceProperties props = L3::DeviceWrapper::getDeviceProperties(device);

    int score = 0;

    uint32_t api_version = props.apiVersion;
    const int major = VK_VERSION_MAJOR(api_version);
    const int minor = VK_VERSION_MINOR(api_version);


    // Базовые баллы
    if (major >= 1) {
        score += BASE_SCORE_V1; // Vulkan 1.0+

        // Бонусы за минорные версии через switch-case
        switch (minor) {
        case 3: score += V1_3_BONUS; break; // 1.3
        case 2: score += V1_2_BONUS; break; // 1.2
        case 1: score += V1_1_BONUS; break; // 1.1
        case 0: break; // 1.0 - без бонусов
        default:       // Для версий выше 1.3
            if (minor > 3) score += V1_3_BONUS;
            break;
        }

        // Проверка фич Vulkan 1.2+
        if (minor >= 2) {
            VkPhysicalDeviceVulkan12Features features12 = {};
            features12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;

            VkPhysicalDeviceFeatures2 features2 = {};
            features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
                features2.pNext = &features12;

            vkGetPhysicalDeviceFeatures2(device, &features2);
            if (features12.shaderFloat16) score += 5;
            if (features12.descriptorIndexing) score += 5;
        }
    }

    return score;
}

prism::PGC::L2::FeatureScores prism::PGC::L2::DeviceRater::calculateFeatureScore(VkPhysicalDevice device)
{
    FeatureScores scores;

    std::vector<VkExtensionProperties> extensions;
    L3::DeviceWrapper::getDeviceExtensionProperties(device, &extensions);

    std::unordered_set<std::string> availableExtensions;
    for (const auto& ext : extensions) {
        availableExtensions.insert(ext.extensionName);
    }

    const std::string swapchainExt = "VK_KHR_swapchain";
    if (availableExtensions.count(swapchainExt)) {
        scores.swapchain = 10.0f;
    }

    // Проверяем Ray Tracing
    const std::vector<std::string> rtExtensions = {
        "VK_KHR_ray_tracing_pipeline",
        "VK_NV_ray_tracing"
    };
    for (const auto& rtExt : rtExtensions) {
        if (availableExtensions.count(rtExt)) {
            scores.raytracing = 25.0f;
            break;
        }
    }

    // Проверяем Upscaling технологии
    const std::vector<std::string> upscalingExtensions = {
        "VK_NV_DLSS",          // NVIDIA DLSS
        "VK_KHR_fragment_shading_rate", // FSR/XeSS
        "VK_EXT_fragment_density_map"   // Альтернативный механизм
    };
    for (const auto& upExt : upscalingExtensions) {
        if (availableExtensions.count(upExt)) {
            scores.upscaling = upExt == "VK_NV_DLSS" ? 20.0f : 15.0f;
            break;
        }

    if (L3::DeviceWrapper::getDeviceProperties(device).apiVersion >= VK_MAKE_VERSION(1, 2, 0)) {
       scores.advancedFeatures += 5.0f;
    }
}

    // Проверяем дополнительные возможности
    VkPhysicalDeviceFeatures2 features2 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 };
    vkGetPhysicalDeviceFeatures2(device, &features2);

    if (features2.features.tessellationShader) {
        scores.advancedFeatures += 5.0f;
    }
    if (features2.features.multiViewport) {
        scores.advancedFeatures += 3.0f;
    }

    return scores;
}

void prism::PGC::L2::ScoreWrapper::print(DeviceScore score)
{
    logger::info("----------------Score----------------");
    logger::info("Type: " + std::to_string(score.typeScore));
    logger::info("Features: " + std::to_string(score.featureScore));
    logger::info("Hardware: " + std::to_string(score.hardwareScore));
    logger::info("API: " + std::to_string(score.apiScore));
    logger::info("TOTAL: " + std::to_string(getTotal(score)));
}

void prism::PGC::L2::ScoreWrapper::print(FeatureScores score)
{
    logger::info("----------------FeatureScore----------------");
    logger::info("Swapchain: " + std::to_string(score.swapchain));
    logger::info("Ray Tracing: " + std::to_string(score.raytracing));
    logger::info("Upscaling: " + std::to_string(score.upscaling));
    logger::info("Advanced Features: " + std::to_string(score.advancedFeatures));
    logger::info("Total Features: " + std::to_string(getTotal(score)));
}

void prism::PGC::L2::ScoreWrapper::print(HardwareScore score)
{
    logger::info("----------------HardwareScore----------------");
    logger::info("VRAM (GB): " + std::to_string(score.vramGB));
    logger::info("Shader Cores (log2): " + std::to_string(score.shaderCores));
    logger::info("Clock Speed (GHz): " + std::to_string(score.clockSpeedGHz));
    logger::info("Texture Units: " + std::to_string(score.textureUnits));
    logger::info("Normalized Score: " + std::to_string(getTotal(score)));
}

int prism::PGC::L2::ScoreWrapper::getTotal(DeviceScore score)
{
    return score.typeScore + score.featureScore + score.hardwareScore + score.apiScore;
}

int prism::PGC::L2::ScoreWrapper::getTotal(FeatureScores score)
{
    return score.swapchain + score.raytracing + score.upscaling + score.advancedFeatures;
}

int prism::PGC::L2::ScoreWrapper::getTotal(HardwareScore score)
{

    float normalizedVram = std::min(score.vramGB / score.REFERENCE_VRAM_GB, 1.0f);
    float normalizedShaderCores = std::min(score.shaderCores / score.REFERENCE_SHADER_CORES, 1.0f);
    float normalizedClockSpeed = std::min(score.clockSpeedGHz / score.REFERENCE_CLOCK_SPEED_GHZ, 1.0f);

    float total = normalizedVram        * score.VRAM_WEIGHT +
                  normalizedShaderCores * score.SHADER_CORES_WEIGHT +
                  normalizedClockSpeed  * score.CLOCK_SPEED_WEIGHT;


    return static_cast<int>(std::clamp(total * 100.0f, 0.0f, 100.0f));
}