Skip to content

Commit

Permalink
Benchmarking utils
Browse files Browse the repository at this point in the history
  • Loading branch information
shg8 committed Apr 11, 2024
1 parent fe59044 commit 8441702
Show file tree
Hide file tree
Showing 14 changed files with 25,086 additions and 51 deletions.
4 changes: 2 additions & 2 deletions apps/viewer/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ int main(int argc, char** argv)

if (benchmarkOutputFolderFlag)
{
config.benchmarkOutputFolder = std::make_optional(args::get(benchmarkOutputFolderFlag));
config.benchmarkOutput = std::make_optional(args::get(benchmarkOutputFolderFlag));
}

auto width = widthFlag ? args::get(widthFlag) : 1280;
Expand All @@ -116,7 +116,7 @@ int main(int argc, char** argv)
#ifndef DEBUG
try {
#endif
auto renderer = VulkanSplatting(std::move(config), VulkanSplatting::createGlfwWindow("Vulkan Splatting", width, height, immediateSwapchain));
auto renderer = VulkanSplatting(std::move(config), VulkanSplatting::createGlfwWindow("Vulkan Splatting", width, height, immediateSwapchain, config.benchmarkOutput.has_value()));
renderer.start();
#ifndef DEBUG
} catch (const std::exception& e) {
Expand Down
4 changes: 2 additions & 2 deletions include/3dgs/3dgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class VulkanSplatting {
bool enableVulkanValidationLayers = false;
std::optional<uint8_t> physicalDeviceId = std::nullopt;
std::string scene;
std::optional<std::string> benchmarkOutputFolder = std::nullopt;
std::optional<std::string> benchmarkOutput = std::nullopt;

float fov = 45.0f;
float near = 0.2f;
Expand All @@ -27,7 +27,7 @@ class VulkanSplatting {
explicit VulkanSplatting(RendererConfiguration configuration, std::shared_ptr<RenderTarget> renderTarget);

#ifdef VKGS_ENABLE_GLFW
static std::shared_ptr<RenderTarget> createGlfwWindow(std::string name, int width, int height, bool immediate);
static std::shared_ptr<RenderTarget> createGlfwWindow(std::string name, int width, int height, bool immediate, bool fixedSize = false);
#endif

#ifdef VKGS_ENABLE_METAL
Expand Down
52 changes: 52 additions & 0 deletions scripts/plot_benchmark.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import pandas as pd
import matplotlib.pyplot as plt

# Ask for the path to the CSV file
path = input('Enter the path to the CSV file: ')
df = pd.read_csv(path)

# Convert nanoseconds to milliseconds
for col in ['preprocess', 'prefix_sum', 'preprocess_sort', 'sort', 'tile_boundary', 'render']:
df[col] = df[col] / 1e6 # Convert from ns to ms

# Sort DataFrame by num_instances
df = df.sort_values(by='num_instances')

# Order the kernels according to the execution sequence
kernel_sequence = ['preprocess', 'prefix_sum', 'preprocess_sort', 'sort', 'tile_boundary', 'render']

# Calculate cumulative sums for the specified order
df_cumulative = df.copy()
for i, kernel in enumerate(kernel_sequence):
if i == 0:
df_cumulative[kernel + '_cumulative'] = df_cumulative[kernel]
else:
df_cumulative[kernel + '_cumulative'] = df_cumulative[kernel_sequence[i-1] + '_cumulative'] + df_cumulative[kernel]

# Plotting
plt.figure(figsize=(10, 6))

# Collect line plots to access their colors for the fill
line_plots = []

# Plot lines for all kernels to store their colors
for kernel in kernel_sequence:
cumulative_column = kernel + '_cumulative'
line_plot, = plt.plot(df_cumulative['num_instances'], df_cumulative[cumulative_column], label=kernel)
line_plots.append(line_plot)

plt.fill_between(df_cumulative['num_instances'], 0, df_cumulative[kernel_sequence[0] + '_cumulative'], color=line_plots[0].get_color(), alpha=0.3)

# Fill the area with the color of the line above
for i in range(len(line_plots)-1):
plt.fill_between(df_cumulative['num_instances'], df_cumulative[kernel_sequence[i] + '_cumulative'], df_cumulative[kernel_sequence[i+1] + '_cumulative'], color=line_plots[i+1].get_color(), alpha=0.3)

plt.xlabel('Number of Instances')
plt.ylabel('Milliseconds')
plt.title('Cumulative Duration of Compute Kernels')
plt.legend()
plt.grid(True)
plt.show()

# Save the plot
plt.savefig('cumulative_duration.png')
33 changes: 33 additions & 0 deletions scripts/run_benchmarks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import os
import subprocess

# Ask for the folder and executable if not in environment variables
folder_path = os.getenv("MODEL_FOLDER_PATH")
executable_path = os.getenv("3DGS_VIEWER_PATH")

resolutions = input("Please enter the resolutions (separated by comma): ")
resolutions = resolutions.split(',')

if not folder_path:
folder_path = input("Please enter the folder containing model folders: ")
if not executable_path:
executable_path = input("Please enter the 3dgs_viewer path: ")

# List the files in the folder
files_in_folder = os.listdir(folder_path)

# Run the command for each file
for file in files_in_folder:
# skip non-folders
if not os.path.isdir(os.path.join(folder_path, file)):
continue

for resolution in resolutions:
width, height = resolution.split('x')

file_path = os.path.join(folder_path, file)
basename = os.path.basename(file_path) + "_" + resolution
print(f"Running benchmark for {basename}")
command = [executable_path, "--no-gui", "--width", width, "--height", height, "-i", "-v", "--validation", "-b",
"../benchmark_output/" + basename + ".csv", "--", file_path]
subprocess.run(command)
4 changes: 2 additions & 2 deletions src/3dgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ VulkanSplatting::VulkanSplatting(RendererConfiguration configuration,

}

std::shared_ptr<RenderTarget> VulkanSplatting::createGlfwWindow(std::string name, int width, int height, bool immediate) {
return std::make_shared<GLFWWindow>(name, width, height, immediate);
std::shared_ptr<RenderTarget> VulkanSplatting::createGlfwWindow(std::string name, int width, int height, bool immediate, bool fixedSize) {
return std::make_shared<GLFWWindow>(name, width, height, immediate, fixedSize);
}
#endif

Expand Down
111 changes: 100 additions & 11 deletions src/GSScene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
#include "spdlog/spdlog.h"
#include "vulkan/Shader.h"

#include "third_party/json.hpp"

#include <glm/gtc/quaternion.hpp>

using json = nlohmann::json;

struct VertexStorage {
glm::vec3 position;
glm::vec3 normal;
Expand All @@ -23,6 +29,53 @@ struct VertexStorage {
glm::vec4 rotation;
};

void GSScene::parseCameras(std::filesystem::path::iterator::reference path) {
std::ifstream camerasFile(path);
json data = json::parse(camerasFile);

for (const auto &[key, value]: data.items()) {
auto position = value["position"];
auto rotation = value["rotation"];
auto rotMat = glm::mat3(
glm::vec3(rotation[0][0], rotation[1][0], rotation[2][0]),
-glm::vec3(rotation[0][1], rotation[1][1], rotation[2][1]),
-glm::vec3(rotation[0][2], rotation[1][2], rotation[2][2])
);
cameras.emplace_back(glm::vec3(position[0], position[1], position[2]), glm::quat(rotMat));
}
}

void GSScene::findPlyFile(std::filesystem::path::iterator::reference path) {
// ply files are under point_cloud/iteration_*/point_cloud.ply
// find the latest iteration
std::filesystem::path latestIteration;
int latestIterationNumber = -1;
for (const auto &entry: std::filesystem::directory_iterator(path / "point_cloud")) {
if (entry.is_directory()) {
auto iteration = entry.path().filename().string();
try {
// substr(10) to remove "iteration_" prefix
int iterationNumber = std::stoi(iteration.substr(10));
if (iterationNumber > latestIterationNumber) {
latestIterationNumber = iterationNumber;
latestIteration = entry.path();
}
} catch (std::invalid_argument &e) {
// ignore
}
}
}

filename = latestIteration / "point_cloud.ply";

spdlog::info("Found ply file: {}", filename);

if (!std::filesystem::exists(filename)) {
spdlog::critical("File does not exist: {}", filename);
throw std::runtime_error("File does not exist: " + filename);
}
}

void GSScene::load(const std::shared_ptr<VulkanContext>&context) {
auto startTime = std::chrono::high_resolution_clock::now();

Expand All @@ -33,42 +86,78 @@ void GSScene::load(const std::shared_ptr<VulkanContext>&context) {
auto camerasPath = path / "cameras.json";
if (std::filesystem::exists(camerasPath)) {
spdlog::info("Dataset folder detected, loading cameras.json");
assert(false && "Not implemented");

parseCameras(camerasPath);
} else {
spdlog::critical("cameras.json not found in dataset folder: {}", camerasPath.string());
throw std::runtime_error("cameras.json not found in dataset folder: " + camerasPath.string());
}
findPlyFile(path);
}

std::ifstream plyFile(filename, std::ios::binary);
loadPlyHeader(plyFile);

vertexBuffer = createBuffer(context, header.numVertices * sizeof(Vertex));
auto vertexStagingBuffer = Buffer::staging(context, header.numVertices * sizeof(Vertex));
auto* verteces = static_cast<Vertex *>(vertexStagingBuffer->allocation_info.pMappedData);
auto* vertexStagingBufferPtr = static_cast<Vertex *>(vertexStagingBuffer->allocation_info.pMappedData);

std::vector<Vertex> vertices(header.numVertices);

auto posMin = glm::vec3(std::numeric_limits<float>::max());
auto posMax = glm::vec3(std::numeric_limits<float>::min());

for (auto i = 0; i < header.numVertices; i++) {
static_assert(sizeof(VertexStorage) == 62 * sizeof(float));
assert(plyFile.is_open());
assert(!plyFile.eof());
VertexStorage vertexStorage;
plyFile.read(reinterpret_cast<char *>(&vertexStorage), sizeof(VertexStorage));
verteces[i].position = glm::vec4(vertexStorage.position, 1.0f);
vertices[i].position = glm::vec4(vertexStorage.position, 1.0f);
posMin = glm::min(posMin, vertexStorage.position);
posMax = glm::max(posMax, vertexStorage.position);
// verteces[i].normal = glm::vec4(vertexStorage.normal, 0.0f);
verteces[i].scale_opacity = glm::vec4(glm::exp(vertexStorage.scale), 1.0f / (1.0f + std::exp(-vertexStorage.opacity)));
verteces[i].rotation = normalize(vertexStorage.rotation);
vertices[i].scale_opacity = glm::vec4(glm::exp(vertexStorage.scale), 1.0f / (1.0f + std::exp(-vertexStorage.opacity)));
vertices[i].rotation = normalize(vertexStorage.rotation);
// memcpy(verteces[i].shs, vertexStorage.shs, 48 * sizeof(float));
verteces[i].shs[0] = vertexStorage.shs[0];
verteces[i].shs[1] = vertexStorage.shs[1];
verteces[i].shs[2] = vertexStorage.shs[2];
vertices[i].shs[0] = vertexStorage.shs[0];
vertices[i].shs[1] = vertexStorage.shs[1];
vertices[i].shs[2] = vertexStorage.shs[2];
auto SH_N = 16;
for (auto j = 1; j < SH_N; j++) {
verteces[i].shs[j * 3 + 0] = vertexStorage.shs[(j - 1) + 3];
verteces[i].shs[j * 3 + 1] = vertexStorage.shs[(j - 1) + SH_N + 2];
verteces[i].shs[j * 3 + 2] = vertexStorage.shs[(j - 1) + SH_N * 2 + 1];
vertices[i].shs[j * 3 + 0] = vertexStorage.shs[(j - 1) + 3];
vertices[i].shs[j * 3 + 1] = vertexStorage.shs[(j - 1) + SH_N + 2];
vertices[i].shs[j * 3 + 2] = vertexStorage.shs[(j - 1) + SH_N * 2 + 1];
}
assert(vertexStorage.normal.x == 0.0f);
assert(vertexStorage.normal.y == 0.0f);
assert(vertexStorage.normal.z == 0.0f);
}

std::sort(vertices.begin(), vertices.end(), [&](const Vertex &a, const Vertex &b) {
auto relAPos = (glm::vec3(a.position) - posMin) / (posMax - posMin);
auto relBPos = (glm::vec3(b.position) - posMin) / (posMax - posMin);
auto scaledRelAPos = static_cast<float>((1 << 21) - 1) * relAPos;
auto scaledRelBPos = static_cast<float>((1 << 21) - 1) * relBPos;

uint64_t codeA = 0;
uint64_t codeB = 0;
for (auto i = 0; i < 21; i++) {
codeA |= (static_cast<uint64_t>(scaledRelAPos.x) & (1 << i)) << (i * 2 + 0);
codeA |= (static_cast<uint64_t>(scaledRelAPos.y) & (1 << i)) << (i * 2 + 1);
codeA |= (static_cast<uint64_t>(scaledRelAPos.z) & (1 << i)) << (i * 2 + 2);

codeB |= (static_cast<uint64_t>(scaledRelBPos.x) & (1 << i)) << (i * 2 + 0);
codeB |= (static_cast<uint64_t>(scaledRelBPos.y) & (1 << i)) << (i * 2 + 1);
codeB |= (static_cast<uint64_t>(scaledRelBPos.z) & (1 << i)) << (i * 2 + 2);
}

return codeA < codeB;
});

// copy vertices to staging buffer
memcpy(vertexStagingBufferPtr, vertices.data(), header.numVertices * sizeof(Vertex));

vertexBuffer->uploadFrom(vertexStagingBuffer);

auto endTime = std::chrono::high_resolution_clock::now();
Expand Down
12 changes: 12 additions & 0 deletions src/GSScene.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <filesystem>
#include <iostream>
#include <glm/glm.hpp>
#include <glm/detail/type_quat.hpp>

#include "vulkan/VulkanContext.h"
#include "vulkan/Buffer.h"

Expand All @@ -22,6 +24,11 @@ struct PlyHeader {

class GSScene {
public:
struct Camera {
glm::vec3 position;
glm::quat rotation;
};

explicit GSScene(const std::string& filename)
: filename(filename) {
// check if file exists
Expand All @@ -30,6 +37,10 @@ class GSScene {
}
}

void parseCameras(std::filesystem::path::iterator::reference path);

void findPlyFile(std::filesystem::path::iterator::reference path);

void load(const std::shared_ptr<VulkanContext>& context);

void loadTestScene(const std::shared_ptr<VulkanContext>& context);
Expand All @@ -51,6 +62,7 @@ class GSScene {

std::shared_ptr<Buffer> vertexBuffer;
std::shared_ptr<Buffer> cov3DBuffer;
std::vector<Camera> cameras;
private:
std::string filename;
PlyHeader header;
Expand Down
Loading

0 comments on commit 8441702

Please sign in to comment.