This commit is contained in:
Martin Slachta
2026-07-18 14:31:15 +02:00
commit a04f0dc262
3343 changed files with 1140208 additions and 0 deletions
+41
View File
@@ -0,0 +1,41 @@
project(loft_render_graph_tests)
find_package(Vulkan QUIET)
find_package(SDL2 REQUIRED)
find_package(Catch2 REQUIRED)
set(LIBS
loft_render_graph
loft_base
loft_common
loft_window
${SDL2_LIBRARIES}
volk)
set(FILES
# ./DependencyGraphTests.cpp
./RenderGraphBuilderTests.cpp
./TopologicalSortTests.cpp
)
add_library(render_graph_unit_tests_sources OBJECT Mock.cpp ${FILES})
target_link_libraries(render_graph_unit_tests_sources
Catch2::Catch2WithMain
${LIBS}
)
add_executable(render_graph_unit_tests)
target_link_libraries(render_graph_unit_tests
PRIVATE
${LIBS}
render_graph_unit_tests_sources
Catch2::Catch2WithMain
)
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(CTest)
include(Catch)
catch_discover_tests(render_graph_unit_tests)
@@ -0,0 +1,40 @@
#include "AdjacencyMatrix.hpp"
#include <set>
void test_color_dependency() {
AdjacencyMatrix adj({"task1", "task2", "final"});
adj.set("task1", "task2");
adj.set("task2", "final");
auto deps = adj.get_dependencies("final");
ASSERT(deps == std::vector<std::string>({"task2"}));
deps = adj.get_dependencies("task2");
ASSERT(deps == std::vector<std::string>({"task1"}));
deps = adj.get_dependencies("task1");
ASSERT(deps == std::vector<std::string>({}));
}
void test_color_dependency2() {
AdjacencyMatrix adj({"task1", "task2", "final"});
adj.set("task1", "final");
adj.set("task2", "final");
auto deps = adj.get_dependencies("final");
ASSERT(std::set<std::string>(deps.begin(), deps.end()) == std::set<std::string>({"task1", "task2"}));
deps = adj.get_dependencies("task2");
ASSERT(deps == std::vector<std::string>({}));
deps = adj.get_dependencies("task1");
ASSERT(deps == std::vector<std::string>({}));
}
int main() {
test_color_dependency();
test_color_dependency2();
return 0;
}
+72
View File
@@ -0,0 +1,72 @@
#include "Mock.hpp"
void lft_dbg_callback(lft::dbg::LogMessageSeverity severity,
lft::dbg::LogMessageType type,
const char *__restrict format,
va_list args) {
}
std::unique_ptr<Gpu> create_mock_gpu() {
auto instance = std::make_unique<const Instance>(
"loft", "loft",
std::vector<std::string>({VK_KHR_SURFACE_EXTENSION_NAME}),
std::vector<std::string>(),
lft_dbg_callback);
volkLoadInstance(instance->instance());
return std::make_unique<Gpu>(instance.get(), std::nullopt);
}
ImageChain create_mock_image_chain(
const Gpu* gpu,
uint32_t num_images,
VkExtent2D extent,
VkFormat format
) {
std::vector<ImageView> images(num_images);
for(uint32_t i = 0; i < num_images; i++) {
MemoryAllocationInfo memory_info = {
.usage = MEMORY_USAGE_AUTO_PREFER_DEVICE
};
ImageCreateInfo image_info = {
.extent = extent,
.format = format,
.usage = VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.arrayLayers = 1,
.mipLevels = 1,
};
Image image = {};
gpu->memory()->create_image(&image_info, &memory_info, &image);
ImageView view = {};
view = image.create_view(gpu, format, {
.aspectMask = image_info.aspectMask,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
});
images[i] = view;
}
return ImageChain(format, extent, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, images);
}
lft::rg::RenderTaskBuilder create_empty_task(const std::string& name) {
return lft::rg::render_task<EmptyContext>(
"task1", new EmptyContext,
[](const lft::rg::TaskBuildInfo& info, EmptyContext* ctx) {},
[](const lft::rg::TaskRecordInfo& info, EmptyContext* ctx) {}
);
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#ifndef MOCK_DEFINED
#define MOCK_DEFINED 1
#include <memory>
#include "Gpu.hpp"
#include "ImageChain.hpp"
#include "RenderGraphBuilder.hpp"
std::unique_ptr<Gpu> create_mock_gpu();
ImageChain create_mock_image_chain(
const Gpu* gpu,
uint32_t num_images,
VkExtent2D extent,
VkFormat format
);
struct EmptyContext { };
lft::rg::RenderTaskBuilder create_empty_task(const std::string& name);
#endif
@@ -0,0 +1,613 @@
#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();
// }
@@ -0,0 +1,166 @@
#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
};
/**
* Test single task
*/
TEST_CASE("SingleTask", "[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()
.build();
builder.add_task(task1);
auto sorted = topology_sort(builder.m_tasks, "output");
REQUIRE(sorted.size() == 1);
REQUIRE(sorted[0].name() == "task1");
}
/**
* Test two tasks where second depends on the first
*/
void test_topological_sort_01() {
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 = lft::rg::render_task<EmptyContext>(
"task1", &ctx,
[](const lft::rg::TaskBuildInfo& info, EmptyContext* ctx) {},
[](const lft::rg::TaskRecordInfo& info, EmptyContext* ctx) {}
).add_color_output("resource1", FMT, EXTENT, {})
.build();
auto task2 = lft::rg::render_task<EmptyContext>(
"task2", &ctx,
[](const lft::rg::TaskBuildInfo& info, EmptyContext* ctx) {},
[](const lft::rg::TaskRecordInfo& info, EmptyContext* ctx) {}
).add_dependency("resource1")
.add_color_output("output", FMT, EXTENT, {})
.build();
builder.add_task(task1);
builder.add_task(task2);
auto sorted = topology_sort(builder.m_tasks, "output");
ASSERT(sorted.size() == 2);
ASSERT(sorted[0].name() == "task1");
ASSERT(sorted[1].name() == "task2");
}
void test_topological_sort_02() {
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 = lft::rg::render_task<EmptyContext>(
"task1", &ctx,
[](const lft::rg::TaskBuildInfo& info, EmptyContext* ctx) {},
[](const lft::rg::TaskRecordInfo& info, EmptyContext* ctx) {}
).add_color_output("resource1", FMT, EXTENT, {})
.build();
auto task2 = lft::rg::render_task<EmptyContext>(
"task2", &ctx,
[](const lft::rg::TaskBuildInfo& info, EmptyContext* ctx) {},
[](const lft::rg::TaskRecordInfo& info, EmptyContext* ctx) {}
).add_dependency("resource1")
.add_color_output("output", FMT, EXTENT, {})
.build();
// inverted - test if it actually sorts
builder.add_task(task2);
builder.add_task(task1);
auto sorted = topology_sort(builder.m_tasks, "output");
ASSERT(sorted.size() == 2);
ASSERT(sorted[0].name() == "task1");
ASSERT(sorted[1].name() == "task2");
}
void test_topological_sort_03() {
auto gpu = create_mock_gpu();
auto image_chain = create_mock_image_chain(gpu.get(), 1, EXTENT, FMT);
EmptyContext ctx;
auto task1 = lft::rg::render_task<EmptyContext>(
"task1", &ctx,
[](const lft::rg::TaskBuildInfo& info, EmptyContext* ctx) {},
[](const lft::rg::TaskRecordInfo& info, EmptyContext* ctx) {}
).add_color_output("resource1", FMT, EXTENT, {})
.build();
auto task2 = lft::rg::render_task<EmptyContext>(
"task2", &ctx,
[](const lft::rg::TaskBuildInfo& info, EmptyContext* ctx) {},
[](const lft::rg::TaskRecordInfo& info, EmptyContext* ctx) {}
).add_color_output("resource2", FMT, EXTENT, {})
.build();
auto task3 = lft::rg::render_task<EmptyContext>(
"task3", &ctx,
[](const lft::rg::TaskBuildInfo& info, EmptyContext* ctx) {},
[](const lft::rg::TaskRecordInfo& info, EmptyContext* ctx) {}
).add_dependency("resource1")
.add_dependency("resource2")
.add_color_output("output", FMT, EXTENT, {})
.build();
lft::rg::Builder builder1(
gpu.get(), image_chain, "output"
);
builder1.add_task(task1);
builder1.add_task(task2);
builder1.add_task(task3);
auto sorted = topology_sort(builder1.m_tasks, "output");
ASSERT(sorted.size() == 3);
ASSERT(sorted[0].name() == "task1" || sorted[0].name() == "task2");
ASSERT(sorted[1].name() == "task1" || sorted[1].name() == "task2");
ASSERT(sorted[0].name() != sorted[1].name());
ASSERT(sorted[2].name() == "task3");
}
// int main() {
// test_topological_sort_01();
// test_topological_sort_02();
// test_topological_sort_03();
//
// return 0;
// }
+5
View File
@@ -0,0 +1,5 @@
int main() {
}