1 /**************************************************************************
2 *
3 * Copyright 2014 Valve Software
4 * Copyright 2015 Google Inc.
5 * All Rights Reserved.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * Author: Jon Ashburn <jon@lunarg.com>
20 * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
21 * Author: Tobin Ehlis <tobin@lunarg.com>
22 * Author: Mark Lobodzinski <mark@lunarg.com>
23 **************************************************************************/
24 #include "vk_layer_config.h"
25 #include "vulkan/vk_sdk_platform.h"
26 #include <fstream>
27 #include <iostream>
28 #include <map>
29 #include <sstream>
30 #include <string.h>
31 #include <string>
32 #include <sys/stat.h>
33 #include <vulkan/vk_layer.h>
34
35 #if defined(_WIN32)
36 #include <windows.h>
37 #endif
38
39 #define MAX_CHARS_PER_LINE 4096
40
41 class ConfigFile {
42 public:
43 ConfigFile();
44 ~ConfigFile();
45
46 const char *getOption(const std::string &_option);
47 void setOption(const std::string &_option, const std::string &_val);
48
49 private:
50 bool m_fileIsParsed;
51 std::map<std::string, std::string> m_valueMap;
52
53 std::string FindSettings();
54 void parseFile(const char *filename);
55 };
56
57 static ConfigFile g_configFileObj;
58
getEnvironment(const char * variable)59 std::string getEnvironment(const char *variable) {
60 #if !defined(__ANDROID__) && !defined(_WIN32)
61 const char *output = getenv(variable);
62 return output == NULL ? "" : output;
63 #elif defined(_WIN32)
64 int size = GetEnvironmentVariable(variable, NULL, 0);
65 if (size == 0) {
66 return "";
67 }
68 char *buffer = new char[size];
69 GetEnvironmentVariable(variable, buffer, size);
70 std::string output = buffer;
71 delete[] buffer;
72 return output;
73 #else
74 return "";
75 #endif
76 }
77
getLayerOption(const char * _option)78 VK_LAYER_EXPORT const char *getLayerOption(const char *_option) { return g_configFileObj.getOption(_option); }
79
80 // If option is NULL or stdout, return stdout, otherwise try to open option
81 // as a filename. If successful, return file handle, otherwise stdout
getLayerLogOutput(const char * _option,const char * layerName)82 VK_LAYER_EXPORT FILE *getLayerLogOutput(const char *_option, const char *layerName) {
83 FILE *log_output = NULL;
84 if (!_option || !strcmp("stdout", _option))
85 log_output = stdout;
86 else {
87 log_output = fopen(_option, "w");
88 if (log_output == NULL) {
89 if (_option)
90 std::cout << std::endl
91 << layerName << " ERROR: Bad output filename specified: " << _option << ". Writing to STDOUT instead"
92 << std::endl
93 << std::endl;
94 log_output = stdout;
95 }
96 }
97 return log_output;
98 }
99
100 // Map option strings to flag enum values
GetLayerOptionFlags(std::string _option,std::unordered_map<std::string,VkFlags> const & enum_data,uint32_t option_default)101 VK_LAYER_EXPORT VkFlags GetLayerOptionFlags(std::string _option, std::unordered_map<std::string, VkFlags> const &enum_data,
102 uint32_t option_default) {
103 VkDebugReportFlagsEXT flags = option_default;
104 std::string option_list = g_configFileObj.getOption(_option.c_str());
105
106 while (option_list.length() != 0) {
107 // Find length of option string
108 std::size_t option_length = option_list.find(",");
109 if (option_length == option_list.npos) {
110 option_length = option_list.size();
111 }
112
113 // Get first option in list
114 const std::string option = option_list.substr(0, option_length);
115
116 auto enum_value = enum_data.find(option);
117 if (enum_value != enum_data.end()) {
118 flags |= enum_value->second;
119 }
120
121 // Remove first option from option_list
122 option_list.erase(0, option_length);
123 // Remove possible comma separator
124 std::size_t char_position = option_list.find(",");
125 if (char_position == 0) {
126 option_list.erase(char_position, 1);
127 }
128 // Remove possible space
129 char_position = option_list.find(" ");
130 if (char_position == 0) {
131 option_list.erase(char_position, 1);
132 }
133 }
134 return flags;
135 }
136
setLayerOption(const char * _option,const char * _val)137 VK_LAYER_EXPORT void setLayerOption(const char *_option, const char *_val) { g_configFileObj.setOption(_option, _val); }
138
139 // Constructor for ConfigFile. Initialize layers to log error messages to stdout by default. If a vk_layer_settings file is present,
140 // its settings will override the defaults.
ConfigFile()141 ConfigFile::ConfigFile() : m_fileIsParsed(false) {
142 m_valueMap["lunarg_core_validation.report_flags"] = "error";
143 m_valueMap["lunarg_object_tracker.report_flags"] = "error";
144 m_valueMap["lunarg_parameter_validation.report_flags"] = "error";
145 m_valueMap["google_threading.report_flags"] = "error";
146 m_valueMap["google_unique_objects.report_flags"] = "error";
147
148 #ifdef WIN32
149 // For Windows, enable message logging AND OutputDebugString
150 m_valueMap["lunarg_core_validation.debug_action"] =
151 "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG,VK_DBG_LAYER_ACTION_DEBUG_OUTPUT";
152 m_valueMap["lunarg_object_tracker.debug_action"] =
153 "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG,VK_DBG_LAYER_ACTION_DEBUG_OUTPUT";
154 m_valueMap["lunarg_parameter_validation.debug_action"] =
155 "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG,VK_DBG_LAYER_ACTION_DEBUG_OUTPUT";
156 m_valueMap["google_threading.debug_action"] =
157 "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG,VK_DBG_LAYER_ACTION_DEBUG_OUTPUT";
158 m_valueMap["google_unique_objects.debug_action"] =
159 "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG,VK_DBG_LAYER_ACTION_DEBUG_OUTPUT";
160 #else // WIN32
161 m_valueMap["lunarg_core_validation.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG";
162 m_valueMap["lunarg_object_tracker.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG";
163 m_valueMap["lunarg_parameter_validation.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG";
164 m_valueMap["google_threading.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG";
165 m_valueMap["google_unique_objects.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG";
166 #endif // WIN32
167
168 m_valueMap["lunarg_core_validation.log_filename"] = "stdout";
169 m_valueMap["lunarg_object_tracker.log_filename"] = "stdout";
170 m_valueMap["lunarg_parameter_validation.log_filename"] = "stdout";
171 m_valueMap["google_threading.log_filename"] = "stdout";
172 m_valueMap["google_unique_objects.log_filename"] = "stdout";
173 }
174
~ConfigFile()175 ConfigFile::~ConfigFile() {}
176
getOption(const std::string & _option)177 const char *ConfigFile::getOption(const std::string &_option) {
178 std::map<std::string, std::string>::const_iterator it;
179 if (!m_fileIsParsed) {
180 std::string settings_file = FindSettings();
181 parseFile(settings_file.c_str());
182 }
183
184 if ((it = m_valueMap.find(_option)) == m_valueMap.end())
185 return "";
186 else
187 return it->second.c_str();
188 }
189
setOption(const std::string & _option,const std::string & _val)190 void ConfigFile::setOption(const std::string &_option, const std::string &_val) {
191 if (!m_fileIsParsed) {
192 std::string settings_file = FindSettings();
193 parseFile(settings_file.c_str());
194 }
195
196 m_valueMap[_option] = _val;
197 }
198
FindSettings()199 std::string ConfigFile::FindSettings() {
200 struct stat info;
201
202 #if defined(WIN32)
203 HKEY hive;
204 LSTATUS err = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Khronos\\Vulkan\\Settings", 0, KEY_READ, &hive);
205 if (err == ERROR_SUCCESS) {
206 char name[2048];
207 DWORD i = 0, name_size = sizeof(name), type, value, value_size = sizeof(value);
208 while (ERROR_SUCCESS ==
209 RegEnumValue(hive, i++, name, &name_size, nullptr, &type, reinterpret_cast<LPBYTE>(&value), &value_size)) {
210 // Check if the registry entry is a dword with a value of zero
211 if (type != REG_DWORD || value != 0) {
212 continue;
213 }
214
215 // Check if this actually points to a file
216 if ((stat(name, &info) != 0) || !(info.st_mode & S_IFREG)) {
217 continue;
218 }
219
220 // Use this file
221 RegCloseKey(hive);
222 return name;
223 }
224
225 RegCloseKey(hive);
226 }
227 #else
228 std::string search_path = getEnvironment("XDG_DATA_HOME");
229 if (search_path == "") {
230 search_path = getEnvironment("HOME");
231 if (search_path != "") {
232 search_path += "/.local/share";
233 }
234 }
235
236 // Use the vk_layer_settings.txt file from here, if it is present
237 if (search_path != "") {
238 std::string home_file = search_path + "/vulkan/settings.d/vk_layer_settings.txt";
239 if (stat(home_file.c_str(), &info) == 0) {
240 if (info.st_mode & S_IFREG) {
241 return home_file;
242 }
243 }
244 }
245
246 #endif
247
248 std::string env_path = getEnvironment("VK_LAYER_SETTINGS_PATH");
249
250 // If the path exists use it, else use vk_layer_settings
251 if (stat(env_path.c_str(), &info) == 0) {
252 // If this is a directory, look for vk_layer_settings within the directory
253 if (info.st_mode & S_IFDIR) {
254 return env_path + "/vk_layer_settings.txt";
255 }
256 return env_path;
257 }
258 return "vk_layer_settings.txt";
259 }
260
parseFile(const char * filename)261 void ConfigFile::parseFile(const char *filename) {
262 std::ifstream file;
263 char buf[MAX_CHARS_PER_LINE];
264
265 m_fileIsParsed = true;
266
267 file.open(filename);
268 if (!file.good()) {
269 return;
270 }
271
272 // read tokens from the file and form option, value pairs
273 file.getline(buf, MAX_CHARS_PER_LINE);
274 while (!file.eof()) {
275 char option[512];
276 char value[512];
277
278 char *pComment;
279
280 // discard any comments delimited by '#' in the line
281 pComment = strchr(buf, '#');
282 if (pComment) *pComment = '\0';
283
284 if (sscanf(buf, " %511[^\n\t =] = %511[^\n \t]", option, value) == 2) {
285 std::string optStr(option);
286 std::string valStr(value);
287 m_valueMap[optStr] = valStr;
288 }
289 file.getline(buf, MAX_CHARS_PER_LINE);
290 }
291 }
292
PrintMessageFlags(VkFlags vk_flags,char * msg_flags)293 VK_LAYER_EXPORT void PrintMessageFlags(VkFlags vk_flags, char *msg_flags) {
294 bool separator = false;
295
296 msg_flags[0] = 0;
297 if (vk_flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
298 strcat(msg_flags, "DEBUG");
299 separator = true;
300 }
301 if (vk_flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
302 if (separator) strcat(msg_flags, ",");
303 strcat(msg_flags, "INFO");
304 separator = true;
305 }
306 if (vk_flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
307 if (separator) strcat(msg_flags, ",");
308 strcat(msg_flags, "WARN");
309 separator = true;
310 }
311 if (vk_flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) {
312 if (separator) strcat(msg_flags, ",");
313 strcat(msg_flags, "PERF");
314 separator = true;
315 }
316 if (vk_flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
317 if (separator) strcat(msg_flags, ",");
318 strcat(msg_flags, "ERROR");
319 }
320 }
321
PrintMessageSeverity(VkFlags vk_flags,char * msg_flags)322 VK_LAYER_EXPORT void PrintMessageSeverity(VkFlags vk_flags, char *msg_flags) {
323 bool separator = false;
324
325 msg_flags[0] = 0;
326 if (vk_flags & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
327 strcat(msg_flags, "VERBOSE");
328 separator = true;
329 }
330 if (vk_flags & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
331 if (separator) strcat(msg_flags, ",");
332 strcat(msg_flags, "INFO");
333 separator = true;
334 }
335 if (vk_flags & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
336 if (separator) strcat(msg_flags, ",");
337 strcat(msg_flags, "WARN");
338 separator = true;
339 }
340 if (vk_flags & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
341 if (separator) strcat(msg_flags, ",");
342 strcat(msg_flags, "ERROR");
343 }
344 }
345
PrintMessageType(VkFlags vk_flags,char * msg_flags)346 VK_LAYER_EXPORT void PrintMessageType(VkFlags vk_flags, char *msg_flags) {
347 bool separator = false;
348
349 msg_flags[0] = 0;
350 if (vk_flags & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) {
351 strcat(msg_flags, "GEN");
352 separator = true;
353 }
354 if (vk_flags & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) {
355 strcat(msg_flags, "SPEC");
356 separator = true;
357 }
358 if (vk_flags & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) {
359 if (separator) strcat(msg_flags, ",");
360 strcat(msg_flags, "PERF");
361 separator = true;
362 }
363 }
364