Skip to content

Commit 44ae6fe

Browse files
committed
Improve error tracking
This would keep track of exactly where the error happened (file and line), so if someone posts an error, it would be a lot easier to spot exactly where the error occurred. Also, it separates errno and VkResult, so I don't have to use awkward VK_ERROR_* values for errors from pthread or libc. Signed-off-by: Shahbaz Youssefi <[email protected]>
1 parent cd41f70 commit 44ae6fe

File tree

30 files changed

+725
-357
lines changed

30 files changed

+725
-357
lines changed

Makefile.am

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,36 @@ ACLOCAL_AMFLAGS = -I m4
33
EXTRA_DIST = COPYING README.md
44

55
bin_PROGRAMS = tut1/tut1
6-
tut1_tut1_SOURCES = tut1/tut1.c tut1/main.c tut1/tut1.h
6+
tut1_tut1_SOURCES = tut1/tut1.c tut1/tut1_error.c tut1/main.c tut1/tut1.h tut1/tut1_error.h
77

88
bin_PROGRAMS += tut2/tut2
99
tut2_tut2_SOURCES = tut2/tut2.c tut2/main.c tut2/tut2.h \
10-
tut1/tut1.c
10+
tut1/tut1.c tut1/tut1_error.c
1111

1212
bin_PROGRAMS += tut3/tut3
1313
tut3_tut3_SOURCES = tut3/tut3.c tut3/main.c tut3/tut3.h \
14-
tut2/tut2.c tut1/tut1.c
14+
tut2/tut2.c tut1/tut1.c tut1/tut1_error.c
1515

1616
bin_PROGRAMS += tut4/tut4
1717
tut4_tut4_SOURCES = tut4/tut4.c tut4/main.c tut4/tut4.h \
18-
tut3/tut3.c tut2/tut2.c tut1/tut1.c
18+
tut3/tut3.c tut2/tut2.c tut1/tut1.c tut1/tut1_error.c
1919

2020
bin_PROGRAMS += tut5/tut5
2121
tut5_tut5_SOURCES = tut5/tut5.c tut5/main.c tut5/tut5.h \
22-
tut2/tut2.c tut1/tut1.c
22+
tut2/tut2.c tut1/tut1.c tut1/tut1_error.c
2323

2424
bin_PROGRAMS += tut6/tut6
2525
tut6_tut6_SOURCES = tut6/tut6.c tut6/main.c tut6/tut6.h \
26-
tut2/tut2.c tut1/tut1.c
26+
tut2/tut2.c tut1/tut1.c tut1/tut1_error.c
2727

2828
bin_PROGRAMS += tut7/tut7
2929
tut7_tut7_SOURCES = tut7/tut7.c tut7/tut7_render.c tut7/main.c tut7/tut7.h tut7/tut7_render.h \
30-
tut6/tut6.c tut4/tut4.c tut3/tut3.c tut2/tut2.c tut1/tut1.c
30+
tut6/tut6.c tut4/tut4.c tut3/tut3.c tut2/tut2.c tut1/tut1.c tut1/tut1_error.c
3131

3232
bin_PROGRAMS += tut8/tut8
3333
tut8_tut8_SOURCES = tut8/tut8.c tut8/tut8_render.c tut8/main.c tut8/tut8.h tut8/tut8_render.h \
34-
tut7/tut7.c tut7/tut7_render.c tut6/tut6.c tut4/tut4.c tut3/tut3.c tut2/tut2.c tut1/tut1.c
34+
tut7/tut7.c tut7/tut7_render.c tut6/tut6.c tut4/tut4.c tut3/tut3.c tut2/tut2.c \
35+
tut1/tut1.c tut1/tut1_error.c
3536

3637
shaderdir = $(datadir)/shaders
3738
shader_DATA = shaders/tut3.comp.spv \

tut1/main.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,34 +49,34 @@ static void print_surprise(const char *indent, const char *who, const char *what
4949

5050
int main(int argc, char **argv)
5151
{
52-
VkResult res;
52+
tut1_error res;
5353
int retval = EXIT_FAILURE;
5454
VkInstance vk;
5555
struct tut1_physical_device devs[MAX_DEVICES];
5656
uint32_t dev_count = MAX_DEVICES;
5757

5858
/* Fire up Vulkan */
5959
res = tut1_init(&vk);
60-
if (res)
60+
if (!tut1_error_is_success(&res))
6161
{
62-
printf("Could not initialize Vulkan: %s\n", tut1_VkResult_string(res));
62+
tut1_error_printf(&res, "Could not initialize Vulkan\n");
6363
goto exit_bad_init;
6464
}
6565

6666
printf("Vulkan is in the house.\n");
6767

6868
/* Take a look at what devices there are */
6969
res = tut1_enumerate_devices(vk, devs, &dev_count);
70-
if (res < 0)
71-
{
72-
printf("Could not enumerate devices: %s\n", tut1_VkResult_string(res));
73-
goto exit_bad_enumerate;
74-
}
75-
else if (res == VK_INCOMPLETE)
70+
if (tut1_error_is_warning(&res))
7671
{
7772
print_surprise("", "you've got", "devices", "dream of");
7873
printf("I have information on only %"PRIu32" of them:\n", dev_count);
7974
}
75+
else if (!tut1_error_is_success(&res))
76+
{
77+
tut1_error_printf(&res, "Could not enumerate devices\n");
78+
goto exit_bad_enumerate;
79+
}
8080
else
8181
printf("I detected the following %"PRIu32" device%s:\n", dev_count, dev_count == 1?"":"s");
8282

tut1/tut1.c

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@
1919

2020
#include "tut1.h"
2121

22-
VkResult tut1_init(VkInstance *vk)
22+
tut1_error tut1_init(VkInstance *vk)
2323
{
24+
tut1_error retval = TUT1_ERROR_NONE;
25+
VkResult res;
26+
2427
/*
2528
* Vulkan is not just for graphics. As the slogan goes, graphics and computation belong together. As a
2629
* result, initialization in Vulkan is rather verbose to allow the application to adapt to a wide set of
@@ -83,7 +86,10 @@ VkResult tut1_init(VkInstance *vk)
8386
* embedded systems debugging or logging. Using custom allocation functions will be explored in a future
8487
* tutorial.
8588
*/
86-
return vkCreateInstance(&info, NULL, vk);
89+
res = vkCreateInstance(&info, NULL, vk);
90+
tut1_error_set_vkresult(&retval, res);
91+
92+
return retval;
8793
}
8894

8995
void tut1_exit(VkInstance vk)
@@ -99,10 +105,11 @@ void tut1_exit(VkInstance vk)
99105
vkDestroyInstance(vk, NULL);
100106
}
101107

102-
VkResult tut1_enumerate_devices(VkInstance vk, struct tut1_physical_device *devs, uint32_t *count)
108+
tut1_error tut1_enumerate_devices(VkInstance vk, struct tut1_physical_device *devs, uint32_t *count)
103109
{
104110
VkPhysicalDevice phy_devs[*count];
105-
VkResult retval;
111+
tut1_error retval = TUT1_ERROR_NONE;
112+
VkResult res;
106113

107114
/*
108115
* A physical device in Vulkan is any actual device with Vulkan capabilities. For example, a physical GPU is
@@ -119,8 +126,9 @@ VkResult tut1_enumerate_devices(VkInstance vk, struct tut1_physical_device *devs
119126
* can't be _that_ many devices and use a fixed upper bound. If there are more devices than I foresaw, then
120127
* VK_INCOMPLETE would be returned, stating that the array doesn't contain all that there is.
121128
*/
122-
retval = vkEnumeratePhysicalDevices(vk, count, phy_devs);
123-
if (retval < 0)
129+
res = vkEnumeratePhysicalDevices(vk, count, phy_devs);
130+
tut1_error_set_vkresult(&retval, res);
131+
if (res < 0)
124132
goto exit_failed;
125133

126134
for (uint32_t i = 0; i < *count; ++i)

tut1/tut1.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@
2222

2323
#include <vulkan/vulkan.h>
2424
#include <stdbool.h>
25+
#include "tut1_error.h"
2526

26-
VkResult tut1_init(VkInstance *vk);
27+
tut1_error tut1_init(VkInstance *vk);
2728
void tut1_exit(VkInstance vk);
2829

2930
#define TUT1_MAX_QUEUE_FAMILY 10
@@ -40,7 +41,7 @@ struct tut1_physical_device
4041
bool queue_families_incomplete;
4142
};
4243

43-
VkResult tut1_enumerate_devices(VkInstance vk, struct tut1_physical_device *devs, uint32_t *count);
44+
tut1_error tut1_enumerate_devices(VkInstance vk, struct tut1_physical_device *devs, uint32_t *count);
4445

4546
const char *tut1_VkResult_string(VkResult res);
4647
const char *tut1_VkPhysicalDeviceType_string(VkPhysicalDeviceType type);

tut1/tut1_error.c

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright (C) 2016 Shahbaz Youssefi <[email protected]>
3+
*
4+
* This file is part of Shabi's Vulkan Tutorials.
5+
*
6+
* Shabi's Vulkan Tutorials is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Shabi's Vulkan Tutorials is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with Shabi's Vulkan Tutorials. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#include <stdio.h>
21+
#include <string.h>
22+
#include <stdarg.h>
23+
#include "tut1_error.h"
24+
#include "tut1.h" /* TODO: once tut1_VkResult_string is moved here, remove this */
25+
26+
/*
27+
* Note: this file handles error tracking and reporting, and has little to do with the tutorials themselves. The main
28+
* purpose here is to track the errors precisely, while keeping the actual tutorials as clean as possible.
29+
*/
30+
31+
void tut1_error_data_set_vkresult(struct tut1_error_data *error, VkResult vkresult, const char *file, unsigned int line)
32+
{
33+
/* If this is not an error, ignore it */
34+
if (vkresult == 0)
35+
return;
36+
37+
/* If error is already set, keep the oldest error, but override warnings */
38+
if (error->type != TUT1_ERROR_SUCCESS && !(error->type == TUT1_ERROR_VKRESULT_WARNING && vkresult < 0))
39+
return;
40+
41+
*error = (struct tut1_error_data){
42+
.type = vkresult < 0?TUT1_ERROR_VKRESULT:TUT1_ERROR_VKRESULT_WARNING,
43+
.vkresult = vkresult,
44+
.file = file,
45+
.line = line,
46+
};
47+
}
48+
49+
void tut1_error_data_set_errno(struct tut1_error_data *error, int err_no, const char *file, unsigned int line)
50+
{
51+
/* If this is not an error, ignore it */
52+
if (err_no == 0)
53+
return;
54+
55+
/* If error is already set, keep the oldest error, but override warnings */
56+
if (error->type != TUT1_ERROR_SUCCESS && error->type != TUT1_ERROR_VKRESULT_WARNING)
57+
return;
58+
59+
*error = (struct tut1_error_data){
60+
.type = TUT1_ERROR_ERRNO,
61+
.err_no = err_no,
62+
.file = file,
63+
.line = line,
64+
};
65+
}
66+
67+
bool tut1_error_data_merge(struct tut1_error_data *error, struct tut1_error_data *other)
68+
{
69+
/* If this is not an error, ignore it */
70+
if (other->type == TUT1_ERROR_SUCCESS)
71+
return false;
72+
73+
/* If error is already set, keep the oldest error, but override warnings */
74+
if (error->type != TUT1_ERROR_SUCCESS && !(error->type == TUT1_ERROR_VKRESULT_WARNING && (other->type == TUT1_ERROR_VKRESULT || other->type == TUT1_ERROR_ERRNO)))
75+
return false;
76+
77+
*error = *other;
78+
return true;
79+
}
80+
81+
bool tut1_error_is_success(struct tut1_error *error)
82+
{
83+
return error->error.type == TUT1_ERROR_SUCCESS;
84+
}
85+
86+
bool tut1_error_is_warning(struct tut1_error *error)
87+
{
88+
return error->error.type == TUT1_ERROR_VKRESULT_WARNING;
89+
}
90+
91+
bool tut1_error_is_error(struct tut1_error *error)
92+
{
93+
return !tut1_error_is_success(error) && !tut1_error_is_warning(error);
94+
}
95+
96+
static void print_error(FILE *fout, struct tut1_error_data *error_data, const char *prefix)
97+
{
98+
fprintf(fout, "%s:%u: %s", error_data->file, error_data->line, prefix);
99+
switch (error_data->type)
100+
{
101+
case TUT1_ERROR_VKRESULT_WARNING:
102+
case TUT1_ERROR_VKRESULT:
103+
fprintf(fout, "%s (VkResult %d)\n", tut1_VkResult_string(error_data->vkresult), error_data->vkresult);
104+
break;
105+
case TUT1_ERROR_ERRNO:
106+
fprintf(fout, "%s (errno %d)\n", strerror(error_data->err_no), error_data->err_no);
107+
break;
108+
default:
109+
fprintf(fout, "<internal error>\n");
110+
break;
111+
}
112+
}
113+
114+
void tut1_error_fprintf(FILE *fout, struct tut1_error *error, const char *fmt, ...)
115+
{
116+
/* if no error, don't print anything */
117+
if (error->error.type == TUT1_ERROR_SUCCESS)
118+
return;
119+
120+
va_list args;
121+
va_start(args, fmt);
122+
vfprintf(fout, fmt, args);
123+
va_end(args);
124+
125+
print_error(fout, &error->error, "");
126+
if (error->sub_error.type != TUT1_ERROR_SUCCESS)
127+
print_error(fout, &error->sub_error, " Resulting from this error: ");
128+
}

tut1/tut1_error.h

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright (C) 2016 Shahbaz Youssefi <[email protected]>
3+
*
4+
* This file is part of Shabi's Vulkan Tutorials.
5+
*
6+
* Shabi's Vulkan Tutorials is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Shabi's Vulkan Tutorials is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with Shabi's Vulkan Tutorials. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#ifndef TUT1_ERROR_H
21+
#define TUT1_ERROR_H
22+
23+
/*
24+
* Note: this file handles error tracking and reporting, and has little to do with the tutorials themselves. The main
25+
* purpose here is to track the errors precisely, while keeping the actual tutorials as clean as possible.
26+
*/
27+
28+
#include <vulkan/vulkan.h>
29+
#include <stdbool.h>
30+
#include <stdio.h>
31+
#include <errno.h>
32+
33+
enum tut1_error_type
34+
{
35+
TUT1_ERROR_SUCCESS = 0,
36+
TUT1_ERROR_VKRESULT,
37+
TUT1_ERROR_VKRESULT_WARNING, /* VK_INCOMPLETE for example */
38+
TUT1_ERROR_ERRNO,
39+
};
40+
41+
struct tut1_error_data
42+
{
43+
enum tut1_error_type type;
44+
union {
45+
VkResult vkresult;
46+
int err_no;
47+
};
48+
const char *file;
49+
unsigned int line;
50+
} tut1_error_data;
51+
52+
typedef struct tut1_error
53+
{
54+
struct tut1_error_data error;
55+
struct tut1_error_data sub_error; /*
56+
* Used in cases where error is e.g. "VK_INCOMPLETE", and it is due to
57+
* another error.
58+
*/
59+
} tut1_error;
60+
61+
#define TUT1_ERROR_NONE (struct tut1_error){ .error = { .type = TUT1_ERROR_SUCCESS, }, .sub_error = { .type = TUT1_ERROR_SUCCESS, }, }
62+
63+
#define tut1_error_set_vkresult(es, e) tut1_error_data_set_vkresult(&(es)->error, (e), __FILE__, __LINE__)
64+
#define tut1_error_set_errno(es, e) tut1_error_data_set_errno (&(es)->error, (e), __FILE__, __LINE__)
65+
#define tut1_error_sub_set_vkresult(es, e) tut1_error_data_set_vkresult(&(es)->sub_error, (e), __FILE__, __LINE__)
66+
#define tut1_error_sub_set_errno(es, e) tut1_error_data_set_errno (&(es)->sub_error, (e), __FILE__, __LINE__)
67+
#define tut1_error_merge(es, os) \
68+
do { \
69+
if (tut1_error_data_merge(&(es)->error, &(os)->error)) \
70+
(es)->sub_error = (os)->sub_error; \
71+
} while (0)
72+
#define tut1_error_sub_merge(es, os) tut1_error_data_merge(&(es)->sub_error, &(os)->error)
73+
74+
void tut1_error_data_set_vkresult(struct tut1_error_data *error, VkResult vkresult, const char *file, unsigned int line);
75+
void tut1_error_data_set_errno(struct tut1_error_data *error, int err_no, const char *file, unsigned int line);
76+
bool tut1_error_data_merge(struct tut1_error_data *error, struct tut1_error_data *other);
77+
78+
bool tut1_error_is_success(struct tut1_error *error);
79+
bool tut1_error_is_warning(struct tut1_error *error);
80+
bool tut1_error_is_error(struct tut1_error *error);
81+
#define tut1_error_printf(es, ...) tut1_error_fprintf(stdout, (es), __VA_ARGS__)
82+
void tut1_error_fprintf(FILE *fout, struct tut1_error *error, const char *fmt, ...) __attribute__((format(printf, 3, 4)));
83+
84+
#endif

0 commit comments

Comments
 (0)