• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

paulmthompson / WhiskerToolbox / 18040375504

26 Sep 2025 02:15PM UTC coverage: 69.77% (+1.2%) from 68.577%
18040375504

push

github

paulmthompson
line selection works across widgets

5 of 20 new or added lines in 3 files covered. (25.0%)

463 existing lines in 11 files now uncovered.

42945 of 61552 relevant lines covered (69.77%)

1128.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.64
/src/WhiskerToolbox/ShaderManager/ShaderManager.cpp
1
#include "ShaderManager.hpp"
2

3
#include "ShaderProgram.hpp"
4

5
#include <QDebug>
6
#include <QFileInfo>
7
#include <QOpenGLContext>
8

9
#include <iostream>
10

11
ShaderManager & ShaderManager::instance() {
518✔
12
    static ShaderManager instance;
518✔
13
    return instance;
518✔
14
}
15

16
ShaderManager::ShaderManager() {
48✔
17
    connect(&m_fileWatcher, &QFileSystemWatcher::fileChanged, this, &ShaderManager::onFileChanged);
48✔
18
}
48✔
19

20
bool ShaderManager::loadProgram(std::string const & name,
208✔
21
                                std::string const & vertexPath,
22
                                std::string const & fragmentPath,
23
                                std::string const & geometryPath,
24
                                ShaderSourceType sourceType) {
25
    QOpenGLContext * ctx = QOpenGLContext::currentContext();
208✔
26
    if (!ctx) {
208✔
27
        std::cerr << "[ShaderManager] No current OpenGL context when loading program: " << name << std::endl;
×
28
        return false;
×
29
    }
30

31
    auto & program_map = m_programs_by_context[ctx];
208✔
32
    auto it_existing = program_map.find(name);
208✔
33
    if (it_existing != program_map.end()) {
208✔
34
        // Already loaded for this context
35
        return true;
18✔
36
    }
37

38
    auto program = std::make_unique<ShaderProgram>(vertexPath, fragmentPath, geometryPath, sourceType);
190✔
39
    if (!program->reload()) {
190✔
40
        std::cerr << "[ShaderManager] Failed to compile shader program: " << name << std::endl;
×
41
        return false;
×
42
    }
43
    program_map[name] = std::move(program);
190✔
44
    m_programSourceType[name] = sourceType;
190✔
45

46
    // Only watch files for hot-reloading if using filesystem
47
    if (sourceType == ShaderSourceType::FileSystem) {
190✔
48
        if (!vertexPath.empty()) m_fileWatcher.addPath(QString::fromStdString(vertexPath));
2✔
49
        if (!fragmentPath.empty()) m_fileWatcher.addPath(QString::fromStdString(fragmentPath));
2✔
50
        if (!geometryPath.empty()) m_fileWatcher.addPath(QString::fromStdString(geometryPath));
2✔
51
        if (!vertexPath.empty()) m_pathToProgramName[vertexPath] = name;
2✔
52
        if (!fragmentPath.empty()) m_pathToProgramName[fragmentPath] = name;
2✔
53
        if (!geometryPath.empty()) m_pathToProgramName[geometryPath] = name;
2✔
54
    } else {
55
        // Print info if resource path (no hot-reload)
56
        std::cout << "[ShaderManager] Loaded shader program '" << name << "' from Qt resource. Hot-reloading is not available." << std::endl;
188✔
57
    }
58
    return true;
190✔
59
}
190✔
60

61
bool ShaderManager::loadComputeProgram(std::string const & name,
3✔
62
                                      std::string const & computePath,
63
                                      ShaderSourceType sourceType) {
64
    QOpenGLContext * ctx = QOpenGLContext::currentContext();
3✔
65
    if (!ctx) {
3✔
UNCOV
66
        std::cerr << "[ShaderManager] No current OpenGL context when loading compute program: " << name << std::endl;
×
UNCOV
67
        return false;
×
68
    }
69

70
    auto & program_map = m_programs_by_context[ctx];
3✔
71
    auto it_existing = program_map.find(name);
3✔
72
    if (it_existing != program_map.end()) {
3✔
73
        // Already loaded for this context
UNCOV
74
        return true;
×
75
    }
76

77
    auto program = std::make_unique<ShaderProgram>(computePath, sourceType);
3✔
78
    if (!program->reload()) {
3✔
79
        std::cerr << "[ShaderManager] Failed to compile compute shader program: " << name << std::endl;
×
80
        return false;
×
81
    }
82
    program_map[name] = std::move(program);
3✔
83
    m_programSourceType[name] = sourceType;
3✔
84

85
    // Only watch files for hot-reloading if using filesystem
86
    if (sourceType == ShaderSourceType::FileSystem) {
3✔
87
        if (!computePath.empty()) m_fileWatcher.addPath(QString::fromStdString(computePath));
×
88
        if (!computePath.empty()) m_pathToProgramName[computePath] = name;
×
89
    } else {
90
        // Print info if resource path (no hot-reload)
91
        std::cout << "[ShaderManager] Loaded compute shader program '" << name << "' from Qt resource. Hot-reloading is not available." << std::endl;
3✔
92
    }
93
    return true;
3✔
94
}
3✔
95

96
ShaderProgram * ShaderManager::getProgram(std::string const & name) {
384✔
97
    QOpenGLContext * ctx = QOpenGLContext::currentContext();
384✔
98
    if (!ctx) {
384✔
99
        return nullptr;
×
100
    }
101
    auto ctx_it = m_programs_by_context.find(ctx);
384✔
102
    if (ctx_it == m_programs_by_context.end()) {
384✔
103
        return nullptr;
3✔
104
    }
105
    auto & program_map = ctx_it->second;
381✔
106
    auto it = program_map.find(name);
381✔
107
    if (it != program_map.end()) {
381✔
108
        return it->second.get();
370✔
109
    }
110
    return nullptr;
11✔
111
}
112

UNCOV
113
void ShaderManager::onFileChanged(QString const & path) {
×
UNCOV
114
    std::string const pathStr = path.toStdString();
×
UNCOV
115
    auto it = m_pathToProgramName.find(pathStr);
×
UNCOV
116
    if (it != m_pathToProgramName.end()) {
×
UNCOV
117
        std::string const & programName = it->second;
×
118
        // Reload the program in all contexts that have it
UNCOV
119
        for (auto & [ctx, program_map]: m_programs_by_context) {
×
120
            (void) ctx;
121
            auto progIt = program_map.find(programName);
×
122
            if (progIt != program_map.end()) {
×
123
                std::cout << "[ShaderManager] Detected change in shader file: " << pathStr << ". Reloading program: " << programName << std::endl;
×
124
                if (progIt->second->reload()) {
×
125
                    std::cout << "[ShaderManager] Successfully reloaded shader program: " << programName << std::endl;
×
126
                    emit shaderReloaded(programName);
×
127
                } else {
128
                    std::cerr << "[ShaderManager] Failed to reload shader program: " << programName << ". Keeping previous version active." << std::endl;
×
129
                }
130
            }
131
        }
132
    }
133
    // Re-add the path to the watcher in case it was removed
UNCOV
134
    if (QFileInfo::exists(path)) {
×
UNCOV
135
        m_fileWatcher.addPath(path);
×
136
    }
UNCOV
137
}
×
138

139
void ShaderManager::cleanup() {
32✔
140
    // Clear all shader programs for all contexts
141
    // Let unique_ptr handle destruction automatically - this will call the safe destructor
142
    m_programs_by_context.clear();
32✔
143
    m_fileWatcher.removePaths(m_fileWatcher.files());
32✔
144
    m_fileWatcher.removePaths(m_fileWatcher.directories());
32✔
145
}
32✔
146

UNCOV
147
void ShaderManager::cleanupCurrentContext() {
×
UNCOV
148
    QOpenGLContext * ctx = QOpenGLContext::currentContext();
×
UNCOV
149
    if (!ctx) return;
×
UNCOV
150
    auto it = m_programs_by_context.find(ctx);
×
UNCOV
151
    if (it == m_programs_by_context.end()) return;
×
152
    // Let unique_ptr handle destruction automatically - this will call the safe destructor
UNCOV
153
    m_programs_by_context.erase(it);
×
154
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc