vkgs-sim / tests /cpp /test_render_graph.cpp
kimjunhan's picture
Add files using upload-large-folder tool
4c75661 verified
Raw History Blame Contribute Delete
9.8 kB
// 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;
}