1/**************************************************************************
2 *
3 * Copyright 2014 Valve Software
4 * Copyright 2015 Google Inc.
5 * All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 *
25 * Author: Jon Ashburn <jon@lunarg.com>
26 * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
27 * Author: Tobin Ehlis <tobin@lunarg.com>
28 **************************************************************************/
29#include <fstream>
30#include <string>
31#include <map>
32#include <string.h>
33#include <vulkan/vk_layer.h>
34#include <iostream>
35#include "vk_layer_config.h"
36#include "vulkan/vk_sdk_platform.h"
37
38#define MAX_CHARS_PER_LINE 4096
39
40class ConfigFile {
41  public:
42    ConfigFile();
43    ~ConfigFile();
44
45    const char *getOption(const std::string &_option);
46    void setOption(const std::string &_option, const std::string &_val);
47
48  private:
49    bool m_fileIsParsed;
50    std::map<std::string, std::string> m_valueMap;
51
52    void parseFile(const char *filename);
53};
54
55static ConfigFile g_configFileObj;
56
57static VkLayerDbgAction stringToDbgAction(const char *_enum) {
58    // only handles single enum values
59    if (!strcmp(_enum, "VK_DBG_LAYER_ACTION_IGNORE"))
60        return VK_DBG_LAYER_ACTION_IGNORE;
61    else if (!strcmp(_enum, "VK_DBG_LAYER_ACTION_LOG_MSG"))
62        return VK_DBG_LAYER_ACTION_LOG_MSG;
63#ifdef WIN32
64    else if (!strcmp(_enum, "VK_DBG_LAYER_ACTION_DEBUG_OUTPUT"))
65        return VK_DBG_LAYER_ACTION_DEBUG_OUTPUT;
66#endif
67    else if (!strcmp(_enum, "VK_DBG_LAYER_ACTION_BREAK"))
68        return VK_DBG_LAYER_ACTION_BREAK;
69    return (VkLayerDbgAction)0;
70}
71
72static VkFlags stringToDbgReportFlags(const char *_enum) {
73    // only handles single enum values
74    if (!strcmp(_enum, "VK_DEBUG_REPORT_INFO"))
75        return VK_DEBUG_REPORT_INFORMATION_BIT_EXT;
76    else if (!strcmp(_enum, "VK_DEBUG_REPORT_WARN"))
77        return VK_DEBUG_REPORT_WARNING_BIT_EXT;
78    else if (!strcmp(_enum, "VK_DEBUG_REPORT_PERF_WARN"))
79        return VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
80    else if (!strcmp(_enum, "VK_DEBUG_REPORT_ERROR"))
81        return VK_DEBUG_REPORT_ERROR_BIT_EXT;
82    else if (!strcmp(_enum, "VK_DEBUG_REPORT_DEBUG"))
83        return VK_DEBUG_REPORT_DEBUG_BIT_EXT;
84    return (VkFlags)0;
85}
86
87static unsigned int convertStringEnumVal(const char *_enum) {
88    unsigned int ret;
89
90    ret = stringToDbgAction(_enum);
91    if (ret)
92        return ret;
93
94    return stringToDbgReportFlags(_enum);
95}
96
97const char *getLayerOption(const char *_option) { return g_configFileObj.getOption(_option); }
98
99// If option is NULL or stdout, return stdout, otherwise try to open option
100//  as a filename. If successful, return file handle, otherwise stdout
101FILE *getLayerLogOutput(const char *_option, const char *layerName) {
102    FILE *log_output = NULL;
103    if (!_option || !strcmp("stdout", _option))
104        log_output = stdout;
105    else {
106        log_output = fopen(_option, "w");
107        if (log_output == NULL) {
108            if (_option)
109                std::cout << std::endl
110                          << layerName << " ERROR: Bad output filename specified: " << _option << ". Writing to STDOUT instead"
111                          << std::endl
112                          << std::endl;
113            log_output = stdout;
114        }
115    }
116    return log_output;
117}
118
119VkDebugReportFlagsEXT getLayerOptionFlags(const char *_option, uint32_t optionDefault) {
120    VkDebugReportFlagsEXT flags = optionDefault;
121    const char *option = (g_configFileObj.getOption(_option));
122
123    /* parse comma-separated options */
124    while (option) {
125        const char *p = strchr(option, ',');
126        size_t len;
127
128        if (p)
129            len = p - option;
130        else
131            len = strlen(option);
132
133        if (len > 0) {
134            if (strncmp(option, "warn", len) == 0) {
135                flags |= VK_DEBUG_REPORT_WARNING_BIT_EXT;
136            } else if (strncmp(option, "info", len) == 0) {
137                flags |= VK_DEBUG_REPORT_INFORMATION_BIT_EXT;
138            } else if (strncmp(option, "perf", len) == 0) {
139                flags |= VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
140            } else if (strncmp(option, "error", len) == 0) {
141                flags |= VK_DEBUG_REPORT_ERROR_BIT_EXT;
142            } else if (strncmp(option, "debug", len) == 0) {
143                flags |= VK_DEBUG_REPORT_DEBUG_BIT_EXT;
144            }
145        }
146
147        if (!p)
148            break;
149
150        option = p + 1;
151    }
152    return flags;
153}
154
155bool getLayerOptionEnum(const char *_option, uint32_t *optionDefault) {
156    bool res;
157    const char *option = (g_configFileObj.getOption(_option));
158    if (option != NULL) {
159        *optionDefault = convertStringEnumVal(option);
160        res = false;
161    } else {
162        res = true;
163    }
164    return res;
165}
166
167void setLayerOptionEnum(const char *_option, const char *_valEnum) {
168    unsigned int val = convertStringEnumVal(_valEnum);
169    char strVal[24];
170    snprintf(strVal, 24, "%u", val);
171    g_configFileObj.setOption(_option, strVal);
172}
173
174void setLayerOption(const char *_option, const char *_val) { g_configFileObj.setOption(_option, _val); }
175
176ConfigFile::ConfigFile() : m_fileIsParsed(false) {}
177
178ConfigFile::~ConfigFile() {}
179
180const char *ConfigFile::getOption(const std::string &_option) {
181    std::map<std::string, std::string>::const_iterator it;
182    if (!m_fileIsParsed) {
183        parseFile("vk_layer_settings.txt");
184    }
185
186    if ((it = m_valueMap.find(_option)) == m_valueMap.end())
187        return NULL;
188    else
189        return it->second.c_str();
190}
191
192void ConfigFile::setOption(const std::string &_option, const std::string &_val) {
193    if (!m_fileIsParsed) {
194        parseFile("vk_layer_settings.txt");
195    }
196
197    m_valueMap[_option] = _val;
198}
199
200void ConfigFile::parseFile(const char *filename) {
201    std::ifstream file;
202    char buf[MAX_CHARS_PER_LINE];
203
204    m_fileIsParsed = true;
205    m_valueMap.clear();
206
207    file.open(filename);
208    if (!file.good())
209        return;
210
211    // read tokens from the file and form option, value pairs
212    file.getline(buf, MAX_CHARS_PER_LINE);
213    while (!file.eof()) {
214        char option[512];
215        char value[512];
216
217        char *pComment;
218
219        // discard any comments delimited by '#' in the line
220        pComment = strchr(buf, '#');
221        if (pComment)
222            *pComment = '\0';
223
224        if (sscanf(buf, " %511[^\n\t =] = %511[^\n \t]", option, value) == 2) {
225            std::string optStr(option);
226            std::string valStr(value);
227            m_valueMap[optStr] = valStr;
228        }
229        file.getline(buf, MAX_CHARS_PER_LINE);
230    }
231}
232
233void print_msg_flags(VkFlags msgFlags, char *msg_flags) {
234    bool separator = false;
235
236    msg_flags[0] = 0;
237    if (msgFlags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
238        strcat(msg_flags, "DEBUG");
239        separator = true;
240    }
241    if (msgFlags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
242        if (separator)
243            strcat(msg_flags, ",");
244        strcat(msg_flags, "INFO");
245        separator = true;
246    }
247    if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
248        if (separator)
249            strcat(msg_flags, ",");
250        strcat(msg_flags, "WARN");
251        separator = true;
252    }
253    if (msgFlags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) {
254        if (separator)
255            strcat(msg_flags, ",");
256        strcat(msg_flags, "PERF");
257        separator = true;
258    }
259    if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
260        if (separator)
261            strcat(msg_flags, ",");
262        strcat(msg_flags, "ERROR");
263    }
264}
265