Files
Martin Slachta a04f0dc262 initial
2026-07-18 14:31:15 +02:00

614 lines
20 KiB
C++

#pragma once
#include <catch2/catch_test_macros.hpp>
#define private public
#include "Mock.hpp"
const VkFormat FMT = VK_FORMAT_R8G8B8A8_UNORM;
const VkExtent2D EXTENT = {
.width = 1024,
.height = 1024
};
struct Struct {
};
TEST_CASE("BothFinalOutputAndColorOutput", "[rg]") {
auto gpu = create_mock_gpu();
auto image_chain = create_mock_image_chain(gpu.get(), 1, EXTENT, FMT);
lft::rg::Builder builder(
gpu.get(), image_chain, "output"
);
EmptyContext ctx;
auto task1 = create_empty_task("task1")
.set_output_to_final()
.add_color_output("output", FMT)
.build();
builder.add_task(task1);
auto rg = builder.build();
REQUIRE(rg.m_buffers[0]->num_batches() == 1);
REQUIRE(rg.m_buffers[0]->batch(0).tasks[0].pDefinition.name() == "task1");
}
void test_render_graph_push() {
auto gpu = create_mock_gpu();
VkImageView a;
VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
ImageChain image_chain = create_mock_image_chain(gpu.get(), 1, EXTENT, FMT);
lft::rg::Builder builder(gpu.get(), image_chain, "output");
Struct data = {};
auto task1 = lft::rg::render_task<Struct>(
"task1", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("output", FMT, EXTENT, {})
.add_color_output("resource1", FMT, EXTENT, {})
.set_extent(EXTENT)
.build();
builder.add_task(task1);
std::cout << "First build" << std::endl;
auto rg = builder.build();
ASSERT(rg.m_buffers[0]->num_batches() == 1);
ASSERT(rg.m_buffers[0]->batch(0).tasks.size() == 1);
ASSERT(rg.m_buffers[0]->batch(0).tasks[0].pDefinition.equals(task1));
auto task2 = create_empty_task("task2")
.add_color_output("output", FMT, EXTENT, {})
.add_dependency("resource1")
.set_extent(EXTENT)
.build();
builder.add_task(task2);
std::cout << "Second build" << std::endl;
rg = builder.build();
ASSERT(rg.m_buffers[0]->num_batches() == 2);
ASSERT(rg.m_buffers[0]->batch(0).tasks.size() == 1);
ASSERT(rg.m_buffers[0]->batch(0).tasks[0].pDefinition.equals(task1));
ASSERT(rg.m_buffers[0]->batch(1).tasks.size() == 1);
ASSERT(rg.m_buffers[0]->batch(1).tasks[0].pDefinition.equals(task2));
auto deps1 = rg.m_dependency_matrix->get_dependencies(1);
ASSERT(deps1.size() == 1);
ASSERT(deps1[0] == 0);
auto deps2 = rg.m_dependency_matrix->get_dependencies("task2");
ASSERT(deps2.size() == 1);
ASSERT(deps2[0] == "task1");
lft::rg::Builder builder2(gpu.get(), image_chain, "output");
builder2.add_task(task1);
builder2.add_task(task2);
std::cout << "Compare build" << std::endl;
builder2.build();
ASSERT(builder.m_allocator.equals(builder2.m_allocator));
ASSERT(builder2.m_allocator.equals(builder.m_allocator));
}
void test_render_graph_insert_begin() {
VkExtent2D extent = {
.width = 1024,
.height = 1024
};
auto gpu = create_mock_gpu();
VkImageView a;
VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
ImageChain image_chain = create_mock_image_chain(gpu.get(), 1, extent, fmt);
lft::rg::Builder builder(gpu.get(), image_chain, "output");
Struct data = {};
auto task1 = lft::rg::render_task<Struct>(
"task1", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("resource1", fmt, extent, {});
auto task2 = lft::rg::render_task<Struct>(
"task2", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("resource2", fmt, extent, {});
auto task3 = lft::rg::render_task<Struct>(
"task3", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("output", fmt, extent, {})
.add_dependency("resource2");
builder.add_task(task2.build());
builder.add_task(task3.build());
builder.build();
task2.add_dependency("resource1");
builder.add_task(task2.build());
builder.add_task(task1.build());
lft::rg::RenderGraph rg = builder.build();
ASSERT(rg.buffer(0).num_batches() == 3);
ASSERT(rg.buffer(0).batch(0).tasks[0].pDefinition.name() == "task1");
ASSERT(rg.buffer(0).batch(1).tasks[0].pDefinition.name() == "task2");
ASSERT(rg.buffer(0).batch(2).tasks[0].pDefinition.name() == "task3");
}
void test_render_graph_insert_middle() {
VkExtent2D extent = {
.width = 1024,
.height = 1024
};
auto gpu = create_mock_gpu();
VkImageView a;
VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
ImageChain image_chain = create_mock_image_chain(gpu.get(), 1, extent, fmt);
lft::rg::Builder builder(gpu.get(), image_chain, "output");
Struct data = {};
auto task1 = lft::rg::render_task<Struct>(
"task1", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("resource1", fmt, extent, {});
auto task2 = lft::rg::render_task<Struct>(
"task2", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("resource2", fmt, extent, {})
.add_dependency("resource1");
auto task3 = lft::rg::render_task<Struct>(
"task3", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("output", fmt, extent, {})
.add_dependency("resource1");
builder.add_task(task1.build());
builder.add_task(task3.build());
builder.build();
task3.add_dependency("resource2");
builder.add_task(task3.build());
builder.add_task(task2.build());
lft::rg::RenderGraph rg = builder.build();
ASSERT(rg.buffer(0).num_batches() == 3);
ASSERT(rg.buffer(0).batch(0).tasks[0].pDefinition.name() == "task1");
ASSERT(rg.buffer(0).batch(1).tasks[0].pDefinition.name() == "task2");
ASSERT(rg.buffer(0).batch(2).tasks[0].pDefinition.name() == "task3");
}
void test_render_graph_extent() {
VkExtent2D extent = {
.width = 1024,
.height = 1024
};
auto gpu = create_mock_gpu();
VkImageView a;
VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
ImageChain image_chain = create_mock_image_chain(gpu.get(), 1, extent, fmt);
lft::rg::Builder builder(gpu.get(), image_chain, "output");
Struct data = {};
auto task1 = lft::rg::render_task<Struct>(
"task1", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("output", fmt, extent, {})
.add_color_output("resource1", fmt, extent, {})
.build();
builder.add_task(task1);
auto task2 = lft::rg::render_task<Struct>(
"task2", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("output", fmt, extent, {})
.add_dependency("resource1")
.build();
builder.add_task(task2);
lft::rg::RenderGraph rg = builder.build();
ASSERT(rg.buffer(0).num_batches() == 2);
// equals tests the extent
ASSERT(!rg.buffer(0).batch(0).tasks[0].pDefinition.equals(task1));
ASSERT(!rg.buffer(0).batch(1).tasks[0].pDefinition.equals(task2));
ASSERT(rg.buffer(0).batch(0).tasks[0].extent.width == extent.width &&
rg.buffer(0).batch(0).tasks[0].extent.height == extent.height);
ASSERT(rg.buffer(0).batch(1).tasks[0].extent.width == extent.width &&
rg.buffer(0).batch(1).tasks[0].extent.height == extent.height);
}
void test_render_graph_update_renderpass() {
std::cout << "Running update test" << std::endl;
VkExtent2D extent = {
.width = 1024,
.height = 1024
};
auto gpu = create_mock_gpu();
VkImageView a;
VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
ImageChain image_chain = create_mock_image_chain(gpu.get(), 1, extent, fmt);
lft::rg::Builder builder(gpu.get(), image_chain, "output");
Struct data = {};
auto task1 = lft::rg::render_task<Struct>(
"task1", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("output", fmt, extent, {})
.add_color_output("resource1", fmt, extent, {})
.set_extent(extent)
.build();
builder.add_task(task1);
auto task2 = lft::rg::render_task<Struct>(
"task2", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("output", fmt, extent, {})
.add_dependency("resource1")
.set_extent(extent)
.build();
builder.add_task(task2);
lft::rg::RenderGraph rg = builder.build();
// update
task1.add_color_output("resource2", fmt, extent, {});
builder.add_task(task1);
VkRenderPass previous_rp = rg.buffer(0).batch(0).tasks[0].render_pass.render_pass;
auto previous_fb = rg.buffer(0).batch(0).tasks[0].framebuffer;
builder.build();
ASSERT(rg.buffer(0).num_batches() == 2);
auto _task1 = rg.buffer(0).batch(0).tasks[0];
auto _task2 = rg.buffer(0).batch(1).tasks[0];
// equals tests the extent
ASSERT(_task1.pDefinition.equals(task1));
ASSERT(_task2.pDefinition.equals(task2));
ASSERT(_task1.render_pass.render_pass != previous_rp);
ASSERT(_task1.framebuffer != previous_fb);
}
void test_render_graph_remove() {
std::cout << "Running update test" << std::endl;
VkExtent2D extent = {
.width = 1024,
.height = 1024
};
auto gpu = create_mock_gpu();
VkImageView a;
VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
ImageChain image_chain = create_mock_image_chain(gpu.get(), 1, extent, fmt);
lft::rg::Builder builder(gpu.get(), image_chain, "output");
Struct data = {};
auto task1 = lft::rg::render_task<Struct>(
"task1", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("output", fmt, extent, {})
.add_color_output("resource1", fmt, extent, {})
.add_color_output("resource2", fmt, extent, {})
.set_extent(extent)
.build();
builder.add_task(task1);
auto task2 = lft::rg::render_task<Struct>(
"task2", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("output", fmt, extent, {})
.add_dependency("resource1")
.set_extent(extent)
.build();
builder.add_task(task1);
builder.add_task(task2);
auto rg = builder.build();
ASSERT(rg.buffer(0).num_batches() == 2);
ASSERT(rg.buffer(0).batch(0).tasks.size() == 1);
ASSERT(rg.buffer(0).batch(1).tasks.size() == 1);
builder.remove_task("task2");
rg = builder.build();
ASSERT(rg.buffer(0).num_batches() == 1);
ASSERT(rg.buffer(0).batch(0).tasks.size() == 1);
}
/*
* Attempts to add task, build the render graph, remove the task again and rebuild repeatedly.
*/
void test_render_graph_remove_and_add() {
std::cout << "Running update test" << std::endl;
VkExtent2D extent = {
.width = 1024,
.height = 1024
};
auto gpu = create_mock_gpu();
VkImageView a;
VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
ImageChain image_chain = create_mock_image_chain(gpu.get(), 4, extent, fmt);
lft::rg::Builder builder(gpu.get(), image_chain, "output");
Struct data = {};
auto task1 = lft::rg::render_task<Struct>(
"task1", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("output", fmt, extent, {})
.add_color_output("resource1", fmt, extent, {})
.add_color_output("resource2", fmt, extent, {})
.set_extent(extent)
.build();
builder.add_task(task1);
auto task2 = lft::rg::render_task<Struct>(
"task2", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("output", fmt, extent, {})
.add_dependency("resource1")
.set_extent(extent)
.build();
builder.add_task(task1);
builder.add_task(task2);
auto rg = builder.build();
ASSERT(rg.buffer(0).num_batches() == 2);
ASSERT(rg.buffer(0).batch(0).tasks.size() == 1);
ASSERT(rg.buffer(0).batch(1).tasks.size() == 1);
for(uint32_t i = 0; i < 10; i++) {
builder.remove_task("task2");
rg = builder.build();
ASSERT(rg.buffer(0).num_batches() == 1);
ASSERT(rg.buffer(0).batch(0).tasks.size() == 1);
builder.add_task(task2);
rg = builder.build();
ASSERT(rg.buffer(0).num_batches() == 2);
ASSERT(rg.buffer(0).batch(0).tasks.size() == 1);
ASSERT(rg.buffer(0).batch(1).tasks.size() == 1);
}
}
void test_buffer_idxs() {
VkExtent2D extent = {
.width = 1024,
.height = 1024
};
auto gpu = create_mock_gpu();
VkImageView a;
VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
ImageChain image_chain = create_mock_image_chain(gpu.get(), 4, extent, fmt);
lft::rg::Builder builder(gpu.get(), image_chain, "output");
Struct data = {};
auto task1 = lft::rg::render_task<Struct>(
"task1", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {
ASSERT(info.buffer_idx() == 0);
},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("output", fmt, extent, {})
.add_color_output("resource1", fmt, extent, {})
.add_color_output("resource2", fmt, extent, {})
.set_extent(extent)
.build();
auto task2 = lft::rg::render_task<Struct>(
"task2", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {
ASSERT(info.buffer_idx() == 0);
},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("output", fmt, extent, {})
.add_dependency("resource1")
.set_extent(extent)
.build();
builder.add_task(task1);
builder.add_task(task2);
auto rg = builder.build();
ASSERT(rg.buffer(0).index() == 0);
}
void test_compute() {
VkExtent2D extent = {
.width = 1024,
.height = 1024
};
auto gpu = create_mock_gpu();
VkImageView a;
VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
ImageChain image_chain = create_mock_image_chain(gpu.get(), 4, extent, fmt);
lft::rg::Builder builder(gpu.get(), image_chain, "output");
Struct data = {};
bool is_build_func_called = false;
auto task1 = lft::rg::compute_task<Struct>(
"task1", &data,
[&](const lft::rg::TaskBuildInfo& info, Struct* ctx) {
ASSERT(info.buffer_idx() == 0);
is_build_func_called = true;
},
[&](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_buffer_output("resource1", 1000)
.build();
auto task2 = lft::rg::render_task<Struct>(
"task2", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {
ASSERT(info.buffer_idx() == 0);
},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("output", fmt, extent, {})
.add_dependency("resource1")
.set_extent(extent)
.build();
builder.add_task(task1);
builder.add_task(task2);
auto rg = builder.build();
ASSERT(is_build_func_called);
ASSERT(rg.buffer(0).batch(0).tasks[0].pDefinition.name() == "task1");
ASSERT(rg.buffer(0).batch(1).tasks[0].pDefinition.name() == "task2");
}
void test_render_graph2() {
VkExtent2D extent = {
.width = 1024,
.height = 1024
};
auto gpu = create_mock_gpu();
VkImageView a;
VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
ImageChain image_chain = create_mock_image_chain(gpu.get(), 4, extent, fmt);
lft::rg::Builder builder(gpu.get(), image_chain, "output");
Struct data = {};
auto task1 = lft::rg::render_task<Struct>(
"task1", &data,
[&](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[&](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("resource1", VK_FORMAT_R8G8B8A8_UNORM, extent, {0.0, 0.0, 0.0, 0.0})
.build();
auto task2 = lft::rg::render_task<Struct>(
"task2", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_dependency("resource1")
.add_color_output("output", fmt, extent, {})
.build();
auto task3 = lft::rg::compute_task<Struct>(
"task3", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_buffer_output("resource2", 1000)
.build();
auto task4 = lft::rg::render_task<Struct>(
"task4", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("output", fmt, extent, {})
.add_dependency("resource2")
.add_dependency("task2")
.build();
auto task5 = lft::rg::render_task<Struct>(
"task5", &data,
[](const lft::rg::TaskBuildInfo& info, Struct* ctx) {},
[](const lft::rg::TaskRecordInfo& info, Struct* ctx) {})
.add_color_output("output", fmt, extent, {})
.add_dependency("task2")
.build();
builder.add_task(task1);
builder.add_task(task2);
builder.add_task(task3);
builder.add_task(task4);
builder.add_task(task5);
auto rg = builder.build();
auto wait1 = rg.get_wait_semaphores_for(&rg.buffer(0), 0);
ASSERT(rg.buffer(0).batch(0).tasks[0].pDefinition.name() == "task3");
ASSERT(wait1.empty());
auto wait2 = rg.get_wait_semaphores_for(&rg.buffer(0), 1);
ASSERT(rg.buffer(0).batch(1).tasks[0].pDefinition.name() == "task1");
ASSERT(wait2.empty());
auto wait3 = rg.get_wait_semaphores_for(&rg.buffer(0), 2);
ASSERT(rg.buffer(0).batch(2).tasks[0].pDefinition.name() == "task2");
ASSERT(wait3.size() == 1);
ASSERT(wait3[0].semaphore == rg.buffer(0).batch(1).signal);
auto wait4 = rg.get_wait_semaphores_for(&rg.buffer(0), 3);
ASSERT(rg.buffer(0).batch(3).tasks[0].pDefinition.name() == "task5");
ASSERT(wait4.size() == 1);
ASSERT(wait4[0].semaphore == rg.buffer(0).batch(2).signal);
auto wait5 = rg.get_wait_semaphores_for(&rg.buffer(0), 4);
ASSERT(rg.buffer(0).batch(4).tasks[0].pDefinition.name() == "task4");
ASSERT(wait5.size() == 2);
ASSERT(wait5[0].semaphore == rg.buffer(0).batch(2).signal ||
wait5[0].semaphore == rg.buffer(0).batch(0).signal);
ASSERT(wait5[1].semaphore == rg.buffer(0).batch(0).signal ||
wait5[1].semaphore == rg.buffer(0).batch(2).signal);
}
// int main() {
// /* test_render_graph_extent();
// test_render_graph_push();
// test_render_graph_insert_begin();
// test_render_graph_insert_middle();
// test_render_graph_update_renderpass();
// test_render_graph_remove();
// test_buffer_idxs();
// test_compute(); */
// test_render_graph2();
// }