File size: 9,798 Bytes
4c75661
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// src/vkgs/src/render_graph.cpp ์‹œํ—˜ โ€” GPU ์—†์ด. vkCmdPipelineBarrier ๋ฅผ ๊ฐ€์งœ๋กœ ์ •์˜ํ•ด ๊ทธ๋ž˜ํ”„๊ฐ€ ๋„ฃ๋Š” ๋ฐฐ๋ฆฌ์–ด๋ฅผ ๊ธฐ๋กํ•œ๋‹ค.
#include <algorithm>
#include <cstdio>
#include <fstream>
#include <string>
#include <vector>

#include "render_graph.hpp"

struct Rec {
    VkPipelineStageFlags src, dst;
    bool image;
    VkImageLayout old_layout, new_layout;
};
static std::vector<Rec> g_barriers;

extern "C" VKAPI_ATTR void VKAPI_CALL vkCmdPipelineBarrier(VkCommandBuffer, VkPipelineStageFlags src,
                                                           VkPipelineStageFlags dst, VkDependencyFlags, uint32_t,
                                                           const VkMemoryBarrier*, uint32_t, const VkBufferMemoryBarrier*,
                                                           uint32_t n_img, const VkImageMemoryBarrier* img) {
    g_barriers.push_back({src, dst, n_img > 0, n_img ? img->oldLayout : VK_IMAGE_LAYOUT_UNDEFINED,
                          n_img ? img->newLayout : VK_IMAGE_LAYOUT_UNDEFINED});
}

static int fails = 0;
#define CHECK(c)                                                  \
    do {                                                          \
        if (!(c)) {                                               \
            std::printf("FAIL %s:%d  %s\n", __FILE__, __LINE__, #c); \
            ++fails;                                              \
        }                                                         \
    } while (0)

using nlohmann::json;

static std::vector<std::string> order_names(vkgs::RenderGraph& g) {
    std::vector<std::string> o;
    for (int i : g.order()) o.push_back(g.passes()[i].name);
    return o;
}

int main(int argc, char** argv) {
    // 1) ์‹คํ–‰ ์ˆœ์„œ = ์„ ์–ธ ์ˆœ์„œ (๊ณ„์•ฝ โ€” render_graph.hpp). ์„ ์–ธ ์ˆœ์„œ๊ฐ€ ์ ‘๊ทผ ์ˆœ์„œ๋ฅผ ์ •์˜ํ•œ๋‹ค
    json j = json::parse(R"({
      "resources": {"a": {"type": "buffer"}, "img": {"type": "image"}},
      "passes": [
        {"name": "consume", "kind": "x", "uses": [["a", "storage_read"], ["img", "storage_write"]]},
        {"name": "produce", "kind": "x", "uses": [["a", "storage_write"]]},
        {"name": "present", "kind": "none", "uses": [["img", "transfer_src"]]}
      ]})");
    vkgs::RenderGraph g;
    g.load(j);
    auto o = order_names(g);
    CHECK((o == std::vector<std::string>{"consume", "produce", "present"}));

    // 2) ๋…๋ฆฝ ํŒจ์Šค๊ฐ€ ์„ž์—ฌ ์žˆ์–ด๋„ ์„ ์–ธ ์ˆœ์„œ
    json j2 = json::parse(R"({
      "resources": {"a": {"type": "buffer"}, "b": {"type": "buffer"}},
      "passes": [
        {"name": "wa", "kind": "x", "uses": [["a", "storage_write"]]},
        {"name": "wb", "kind": "x", "uses": [["b", "storage_write"]]},
        {"name": "ra", "kind": "x", "uses": [["a", "storage_read"]]},
        {"name": "rb", "kind": "x", "uses": [["b", "storage_read"]]}
      ]})");
    g.load(j2);
    o = order_names(g);
    CHECK((o == std::vector<std::string>{"wa", "wb", "ra", "rb"}));

    // 3) ๋ชจ๋ฅด๋Š” ๋ฆฌ์†Œ์Šคยท์‚ฌ์šฉ ๋ฐฉ์‹์€ ๋กœ๋“œ ๋•Œ ๊ฑฐ๋ถ€
    bool threw = false;
    try {
        g.load(json::parse(R"({"resources": {}, "passes": [{"name": "p", "kind": "x", "uses": [["nope", "storage_read"]]}]})"));
    } catch (const std::exception&) { threw = true; }
    CHECK(threw);
    threw = false;
    try {
        g.load(json::parse(R"({"resources": {"a": {}}, "passes": [{"name": "p", "kind": "x", "uses": [["a", "sideways"]]}]})"));
    } catch (const std::exception&) { threw = true; }
    CHECK(threw);

    // 4) ๋ฐฐ๋ฆฌ์–ด: ์“ฐ๊ธฐ โ†’ ๊ฐ™์€ ๋‹จ๊ณ„ ์ฝ๊ธฐ ํ•œ ๋ฒˆ, ๋‘ ๋ฒˆ์งธ ์ฝ๊ธฐ๋Š” ๋ฐฐ๋ฆฌ์–ด ์—†์Œ, ์ด๋ฏธ์ง€ ๋ ˆ์ด์•„์›ƒ ์ „ํ™˜
    g.load(j2);
    g.begin_frame();
    g_barriers.clear();
    g.traverse(VK_NULL_HANDLE, [](vkgs::RGPass&) {});
    // wa, wb: ์ฒ˜์Œ ์“ฐ๊ธฐ โ€” ์•ž์„  ์ ‘๊ทผ์ด ์—†์–ด ๋ฐฐ๋ฆฌ์–ด ์—†์Œ. ra, rb: RAW ๊ฐ 1๊ฐœ
    CHECK(g_barriers.size() == 2);
    for (const auto& b : g_barriers) CHECK(b.src == VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT && !b.image);

    json j3 = json::parse(R"({
      "resources": {"img": {"type": "image"}},
      "passes": [
        {"name": "w", "kind": "x", "uses": [["img", "storage_write"]]},
        {"name": "r1", "kind": "x", "uses": [["img", "storage_read"]]},
        {"name": "r2", "kind": "x", "uses": [["img", "storage_read"]]},
        {"name": "copy", "kind": "x", "uses": [["img", "transfer_src"]]}
      ]})");
    g.load(j3);
    g.bind_image("img", reinterpret_cast<VkImage>(uintptr_t(1)));
    g.begin_frame();
    g_barriers.clear();
    g.traverse(VK_NULL_HANDLE, [](vkgs::RGPass&) {});
    // w: UNDEFINED โ†’ GENERAL, r1: RAW, r2: ์—†์Œ (์ด๋ฏธ ๋ณด์ž„), copy: RAW + GENERAL โ†’ TRANSFER_SRC
    CHECK(g_barriers.size() == 3);
    if (g_barriers.size() == 3) {
        CHECK(g_barriers[0].old_layout == VK_IMAGE_LAYOUT_UNDEFINED && g_barriers[0].new_layout == VK_IMAGE_LAYOUT_GENERAL);
        CHECK(g_barriers[1].src == VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
        CHECK(g_barriers[2].dst == VK_PIPELINE_STAGE_TRANSFER_BIT &&
              g_barriers[2].new_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
    }

    // 4b) ๊ฐ™์€ ๋‹จ๊ณ„์—์„œ ์ด๋ฏธ ๋ณด์ด๋Š” ์“ฐ๊ธฐ๋ผ๋„ ๋ ˆ์ด์•„์›ƒ์ด ๋ฐ”๋€Œ๋ฉด ๊ทธ ์“ฐ๊ธฐ์™€ ๋™๊ธฐํ™”ํ•œ๋‹ค (์ „ํ™˜ = ์“ฐ๊ธฐ)
    json j4 = json::parse(R"({
      "resources": {"img": {"type": "image"}},
      "passes": [
        {"name": "copy_in", "kind": "x", "uses": [["img", "transfer_dst"]]},
        {"name": "copy_out", "kind": "x", "uses": [["img", "transfer_src"]]}
      ]})");
    g.load(j4);
    g.bind_image("img", reinterpret_cast<VkImage>(uintptr_t(1)));
    g.begin_frame();
    g_barriers.clear();
    g.traverse(VK_NULL_HANDLE, [](vkgs::RGPass&) {});
    CHECK(g_barriers.size() == 2);
    if (g_barriers.size() == 2)
        CHECK(g_barriers[1].src == VK_PIPELINE_STAGE_TRANSFER_BIT &&
              g_barriers[1].new_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);

    // 4c) follows: ์ง ํŒจ์Šค๊ฐ€ ๊บผ์ง€๋ฉด ๊ฐ™์ด ๊ฑด๋„ˆ๋›ด๋‹ค
    json j5 = json::parse(R"({
      "resources": {"a": {"type": "buffer"}},
      "passes": [
        {"name": "lead", "kind": "x", "optional": true, "uses": [["a", "storage_write"]]},
        {"name": "tail", "kind": "x", "follows": "lead", "uses": [["a", "storage_read"]]}
      ]})");
    g.load(j5);
    g.find("lead")->enabled = false;
    g.begin_frame();
    std::vector<std::string> ran5;
    g.traverse(VK_NULL_HANDLE, [&](vkgs::RGPass& p) { ran5.push_back(p.name); });
    CHECK(ran5.empty());

    // 5) ๊บผ์ง„ ํŒจ์Šค๋Š” ๊ฑด๋„ˆ๋›ด๋‹ค
    g.load(j3);
    g.find("r1")->enabled = false;
    g.begin_frame();
    std::vector<std::string> ran;
    g.traverse(VK_NULL_HANDLE, [&](vkgs::RGPass& p) { ran.push_back(p.name); });
    CHECK((ran == std::vector<std::string>{"w", "r2", "copy"}));

    // 5b) follows: ์ง ํŒจ์Šค๊ฐ€ ์ผœ์ ธ ์žˆ์œผ๋ฉด ๊ฐ™์ด ๋ˆ๋‹ค
    g.load(j5);
    g.begin_frame();
    std::vector<std::string> ran5b;
    g.traverse(VK_NULL_HANDLE, [&](vkgs::RGPass& p) { ran5b.push_back(p.name); });
    CHECK((ran5b == std::vector<std::string>{"lead", "tail"}));

    // 5c) ์ƒ‰ ์ฒจ๋ถ€๋กœ ์“ด ์ด๋ฏธ์ง€๋ฅผ ์ปดํ“จํŠธ๊ฐ€ ์ฝ๊ธฐ (RAW) โ€” ๋ Œ๋” ํŒจ์Šค finalLayout(GENERAL) ๊ทธ๋Œ€๋กœ, ์ƒ‰ ์ถœ๋ ฅ ๋‹จ๊ณ„์—์„œ
    json j6 = json::parse(R"({
      "resources": {"img": {"type": "image"}, "dep": {"type": "image"}},
      "passes": [
        {"name": "raster", "kind": "x", "uses": [["img", "color_attachment"], ["dep", "depth_attachment"]]},
        {"name": "read", "kind": "x", "uses": [["img", "storage_read"], ["dep", "storage_read"]]}
      ]})");
    g.load(j6);
    g.bind_image("img", reinterpret_cast<VkImage>(uintptr_t(1)));
    g.bind_image("dep", reinterpret_cast<VkImage>(uintptr_t(2)), VK_IMAGE_ASPECT_DEPTH_BIT);
    g.begin_frame();
    g_barriers.clear();
    g.traverse(VK_NULL_HANDLE, [](vkgs::RGPass&) {});
    CHECK(g_barriers.size() == 2);  // ์ฒจ๋ถ€ ์ž์ฒด์—๋Š” ๋ฐฐ๋ฆฌ์–ด ์—†์Œ (๋ Œ๋” ํŒจ์Šค ๋ชซ), ์ฝ๊ธฐ์— ๊ฐ 1๊ฐœ
    if (g_barriers.size() == 2) {
        CHECK(g_barriers[0].src == VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT &&
              g_barriers[0].old_layout == VK_IMAGE_LAYOUT_GENERAL && g_barriers[0].new_layout == VK_IMAGE_LAYOUT_GENERAL);
        CHECK(g_barriers[1].src == VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT &&
              g_barriers[1].old_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL &&
              g_barriers[1].new_layout == VK_IMAGE_LAYOUT_GENERAL);
    }

    // 6) ์ €์žฅ์†Œ์˜ ์‹ค์ œ ๊ทธ๋ž˜ํ”„๊ฐ€ ๋กœ๋“œยท์ •๋ ฌ๋˜๊ณ  DOT ๋ฅผ ๋งŒ๋“ ๋‹ค
    if (argc > 1) {
        std::ifstream f(argv[1]);
        g.load(json::parse(f, nullptr, true, true));
        o = order_names(g);
        CHECK(o.front() == "clear_draw_args" && o.back() == "to_present");
        CHECK(g.to_dot().find("digraph") != std::string::npos);
        // ์‹ค์ œ ๊ทธ๋ž˜ํ”„ ์ˆœํšŒ: ๊ฐ•์ˆ˜ ๋ ˆ์ด์–ด(์ƒ‰ ์ฒจ๋ถ€) โ†’ ๋‚ ์”จ(์ปดํ“จํŠธ ์ฝ๊ธฐ) ์— ์ƒ‰ ์ถœ๋ ฅ โ†’ ์ปดํ“จํŠธ ๋ฐฐ๋ฆฌ์–ด๊ฐ€ ์žˆ์–ด์•ผ ํ•œ๋‹ค
        int k = 1;
        for (const auto& [name, r] : g.resources())
            if (r.type == "image") g.bind_image(name, reinterpret_cast<VkImage>(uintptr_t(k++)));
        for (auto& p : g.passes()) p.enabled = true;
        g.begin_frame();
        g_barriers.clear();
        std::vector<std::string> ran6;
        g.traverse(VK_NULL_HANDLE, [&](vkgs::RGPass& p) { ran6.push_back(p.name); });
        bool color_to_compute = false;
        for (const auto& b : g_barriers)
            if (b.image && b.src == VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT && (b.dst & VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT))
                color_to_compute = true;
        CHECK(color_to_compute);
        CHECK(std::find(ran6.begin(), ran6.end(), "precip") < std::find(ran6.begin(), ran6.end(), "weather"));
    }
    std::printf("%s\n", fails ? "์‹คํŒจ" : "ํ†ต๊ณผ");
    return fails ? 1 : 0;
}