Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ find_package(Stb REQUIRED)

find_package(spdlog CONFIG REQUIRED)

find_package(imgui CONFIG REQUIRED)

find_path(CGLTF_INCLUDE_DIRS "cgltf.h")

add_compile_definitions(ENABLE_TELEMETRY)
Expand Down
8 changes: 8 additions & 0 deletions imgui.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[Window][Debug##Default]
Pos=60,60
Size=400,400

[Window][Quark]
Pos=349,116
Size=236,88

2 changes: 2 additions & 0 deletions src/backend/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ add_subdirectory(presentation)
add_subdirectory(graphics)
add_subdirectory(gpu)
add_subdirectory(shaders)
add_subdirectory(ui)
add_subdirectory(profiling)

add_library(quark_backend INTERFACE)
Expand All @@ -14,6 +15,7 @@ target_link_libraries(quark_backend INTERFACE
quark::backend::graphics
quark::backend::gpu
quark::backend::shaders
quark::backend::ui
quark::backend::profiling
)

Expand Down
3 changes: 3 additions & 0 deletions src/backend/presentation/vk_presenter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ class VkPresenter {
return static_cast<uint32_t>(m_swapchain.swapchainImageViews().size());
}

[[nodiscard]] GlfwWindow *window() noexcept { return m_window; }
[[nodiscard]] const GlfwWindow *window() const noexcept { return m_window; }

private:
VkBackendCtx *m_ctx = nullptr; // non-owning
GlfwWindow *m_window = nullptr; // non-owning
Expand Down
2 changes: 1 addition & 1 deletion src/backend/profiling/logging/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
add_library(quark_backend_profiling_logging STATIC
profiling_logger.cpp
console_sink.cpp
)

target_include_directories(quark_backend_profiling_logging
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#include "backend/profiling/logging/profiling_logger.hpp"
#include "backend/profiling/logging/console_sink.hpp"

#if defined(ENABLE_TELEMETRY)
#include "backend/profiling/profilers/cpu_profiler.hpp"
#include "backend/profiling/profilers/gpu_frame_stats.hpp"
#include "backend/profiling/profilers/upload_profiler.hpp"
#include "backend/profiling/profilers/vk_gpu_profiler.hpp"
#include "backend/profiling/telemetry/publish.hpp"
#include "backend/profiling/telemetry/telemetry.hpp"

#include <array>
#include <cstddef>
Expand Down Expand Up @@ -39,15 +42,6 @@ static inline void formatBytes(char *out, std::size_t outSize,
out[outSize - 1] = '\0';
}

bool FrameLogger::shouldLog() noexcept {
if (m_period == 0) {
return false;
}

++m_frameCounter;
return (m_frameCounter % m_period) == 0ULL;
}

static inline double msAt(const CpuProfiler::Frame &st,
CpuProfiler::Stat stat) noexcept {
return st.ms[static_cast<size_t>(stat)];
Expand All @@ -67,9 +61,7 @@ static inline void formatMs(char *out, size_t outSize, double ms) noexcept {
}
}

static void logCpu(const CpuProfiler &cpu) noexcept {
const auto &st = cpu.last();

static void logCpuFrame(const CpuProfiler::Frame &cpu) noexcept {
std::array<char, 512> line1{};
std::array<char, 256> line2{};

Expand All @@ -82,16 +74,18 @@ static void logCpu(const CpuProfiler &cpu) noexcept {
std::array<char, 16> pres{};
std::array<char, 16> other{};

formatMs(frame.data(), frame.size(), msAt(st, CpuProfiler::Stat::FrameTotal));
formatMs(acq.data(), acq.size(), msAt(st, CpuProfiler::Stat::Acquire));
formatMs(frame.data(), frame.size(),
msAt(cpu, CpuProfiler::Stat::FrameTotal));
formatMs(acq.data(), acq.size(), msAt(cpu, CpuProfiler::Stat::Acquire));
formatMs(fence.data(), fence.size(),
msAt(st, CpuProfiler::Stat::WaitForFence));
msAt(cpu, CpuProfiler::Stat::WaitForFence));
formatMs(ubo.data(), ubo.size(),
msAt(st, CpuProfiler::Stat::UpdatePerFrameUBO));
formatMs(rec.data(), rec.size(), msAt(st, CpuProfiler::Stat::RecordCmd));
formatMs(sub.data(), sub.size(), msAt(st, CpuProfiler::Stat::QueueSubmit));
formatMs(pres.data(), pres.size(), msAt(st, CpuProfiler::Stat::QueuePresent));
formatMs(other.data(), other.size(), msAt(st, CpuProfiler::Stat::Other));
msAt(cpu, CpuProfiler::Stat::UpdatePerFrameUBO));
formatMs(rec.data(), rec.size(), msAt(cpu, CpuProfiler::Stat::RecordCmd));
formatMs(sub.data(), sub.size(), msAt(cpu, CpuProfiler::Stat::QueueSubmit));
formatMs(pres.data(), pres.size(),
msAt(cpu, CpuProfiler::Stat::QueuePresent));
formatMs(other.data(), other.size(), msAt(cpu, CpuProfiler::Stat::Other));

ignore_snprintf(std::snprintf(
line1.data(), line1.size(),
Expand All @@ -103,88 +97,65 @@ static void logCpu(const CpuProfiler &cpu) noexcept {
ignore_snprintf(std::snprintf(
line2.data(), line2.size(),
"CPU cnt: draws %-6u inst %-6u tris %-8llu pipe %-4u desc %-4u",
st.drawCalls, st.instances, static_cast<unsigned long long>(st.triangles),
st.pipelineBinds, st.descriptorBinds));

std::cerr << "\n[Profiler]\n" << line1.data() << "\n" << line2.data() << "\n";
}

static void logGpu(const VkGpuProfiler &gpu) noexcept {
const auto &gst = gpu.last();
if (!gst.valid) {
return;
}

std::array<char, 16> frame{};
std::array<char, 16> main{};
std::array<char, 16> idle{};
cpu.drawCalls, cpu.instances,
static_cast<unsigned long long>(cpu.triangles), cpu.pipelineBinds,
cpu.descriptorBinds));

formatMs(frame.data(), frame.size(), gst.frameMs);
formatMs(main.data(), main.size(), gst.mainPassMs);
formatMs(idle.data(), idle.size(), gst.idleGapMs);

std::array<char, 256> line{};
ignore_snprintf(std::snprintf(line.data(), line.size(),
"GPU ms: frame %s main %s idle %s",
frame.data(), main.data(), idle.data()));

std::cerr << line.data() << "\n";
std::cerr << line1.data() << "\n" << line2.data() << "\n";
}

static void logUpload(const UploadProfiler &upload) noexcept {
const auto &ust = upload.last();
const auto &lt = upload.lifetime();

static void logUploadFrame(const UploadProfiler::Frame &upload,
const UploadProfiler::Frame &lifetime) noexcept {
const auto idx = [](UploadProfiler::Stat stat) {
return static_cast<std::size_t>(stat);
};

// per frame
const std::uint64_t submitCount =
ust.v[idx(UploadProfiler::Stat::UploadSubmitCount)];
upload.v[idx(UploadProfiler::Stat::UploadSubmitCount)];

const std::uint64_t memcpyCount =
ust.v[idx(UploadProfiler::Stat::UploadMemcpyCount)];
upload.v[idx(UploadProfiler::Stat::UploadMemcpyCount)];
const std::uint64_t memcpyBytes =
ust.v[idx(UploadProfiler::Stat::UploadMemcpyBytes)];
upload.v[idx(UploadProfiler::Stat::UploadMemcpyBytes)];

const std::uint64_t stagingUsedBytes =
ust.v[idx(UploadProfiler::Stat::StagingUsedBytes)];
upload.v[idx(UploadProfiler::Stat::StagingUsedBytes)];

const std::uint64_t bufCount =
ust.v[idx(UploadProfiler::Stat::BufferUploadCount)];
upload.v[idx(UploadProfiler::Stat::BufferUploadCount)];
const std::uint64_t bufBytes =
ust.v[idx(UploadProfiler::Stat::BufferUploadBytes)];
upload.v[idx(UploadProfiler::Stat::BufferUploadBytes)];

const std::uint64_t texCount =
ust.v[idx(UploadProfiler::Stat::TextureUploadCount)];
upload.v[idx(UploadProfiler::Stat::TextureUploadCount)];
const std::uint64_t texBytes =
ust.v[idx(UploadProfiler::Stat::TextureUploadBytes)];
upload.v[idx(UploadProfiler::Stat::TextureUploadBytes)];

const std::uint64_t matCount =
ust.v[idx(UploadProfiler::Stat::MaterialUploadCount)];
upload.v[idx(UploadProfiler::Stat::MaterialUploadCount)];
const std::uint64_t matBytes =
ust.v[idx(UploadProfiler::Stat::MaterialUploadBytes)];
upload.v[idx(UploadProfiler::Stat::MaterialUploadBytes)];

const std::uint64_t instCount =
ust.v[idx(UploadProfiler::Stat::InstanceUploadCount)];
upload.v[idx(UploadProfiler::Stat::InstanceUploadCount)];
const std::uint64_t instBytes =
ust.v[idx(UploadProfiler::Stat::InstanceUploadBytes)];
upload.v[idx(UploadProfiler::Stat::InstanceUploadBytes)];

// lifetime
const std::uint64_t stagingCreatedCount =
lt.v[idx(UploadProfiler::Stat::StagingCreatedCount)];
lifetime.v[idx(UploadProfiler::Stat::StagingCreatedCount)];

const std::uint64_t stagingAllocBytes =
lt.v[idx(UploadProfiler::Stat::StagingAllocatedBytes)];
lifetime.v[idx(UploadProfiler::Stat::StagingAllocatedBytes)];
const std::uint64_t bufAllocBytes =
lt.v[idx(UploadProfiler::Stat::BufferAllocatedBytes)];
lifetime.v[idx(UploadProfiler::Stat::BufferAllocatedBytes)];
const std::uint64_t texAllocBytes =
lt.v[idx(UploadProfiler::Stat::TextureAllocatedBytes)];
lifetime.v[idx(UploadProfiler::Stat::TextureAllocatedBytes)];
const std::uint64_t matAllocBytes =
lt.v[idx(UploadProfiler::Stat::MaterialAllocatedBytes)];
lifetime.v[idx(UploadProfiler::Stat::MaterialAllocatedBytes)];
const std::uint64_t instAllocBytes =
lt.v[idx(UploadProfiler::Stat::InstanceAllocatedBytes)];
lifetime.v[idx(UploadProfiler::Stat::InstanceAllocatedBytes)];

std::array<char, 32> memcpyStr{};
std::array<char, 32> stagingUsedStr{};
Expand Down Expand Up @@ -237,36 +208,44 @@ static void logUpload(const UploadProfiler &upload) noexcept {
std::cerr << line.data() << "\n";
}

void FrameLogger::logPerFrame(const CpuProfiler *cpu, const VkGpuProfiler &gpu,
const UploadProfiler *upload) noexcept {
if (!shouldLog()) {
static void logGpu(const GpuProfiler::Frame &gpu) noexcept {
if (!gpu.valid) {
return;
}

// TODO: add rolling average over N frames and print that on N frame instead
// of just N frame data

// NOTE: if queueSubmit is large its likely artifical wait time
// for vsync from FIFO present mode in swapchain
std::array<char, 16> frame{};
std::array<char, 16> main{};
std::array<char, 16> idle{};

// Note: Most of other is likely from the vkWaitForFence in commands on
// submit immediate. I didn't bother logging this since its a pain to
// pass since mesh_store is the one who calls the uploaders who then
// call submit immediate so I would have to pass profiler a lot. But it
// doesn't matter since eventually submit Immediate will be removed and
// we won't have blocking anymore
logCpu(*cpu);
formatMs(frame.data(), frame.size(), gpu.frameMs);
formatMs(main.data(), main.size(), gpu.mainPassMs);
formatMs(idle.data(), idle.size(), gpu.idleGapMs);

logUpload(*upload);
logGpu(gpu);
std::array<char, 256> line{};
ignore_snprintf(std::snprintf(line.data(), line.size(),
"GPU ms: frame %s main %s idle %s",
frame.data(), main.data(), idle.data()));

std::cout << "\n";
std::cerr << line.data() << "\n";
}

#ifndef NDEBUG
void emit(Event e, double ms) noexcept {
std::cerr << "[Event] " << name(e) << " ms=" << ms << "\n";
void logProfilerToConsole(const Telemetry &t) noexcept {

// TODO: add rolling average over N frames and print that on N frame instead
// of just N frame data
CpuProfiler::Frame cpu{};
UploadProfiler::Frame upl{};
UploadProfiler::Frame uplLt{};
GpuProfiler::Frame gpu{};
if (!readPublished(t, cpu, upl, uplLt, gpu)) {
return;
}

std::cerr << "\n[Profiler]\n";
logCpuFrame(cpu);
logUploadFrame(upl, uplLt);
logGpu(gpu);
}
#endif

} // namespace profiling
#endif
15 changes: 15 additions & 0 deletions src/backend/profiling/logging/console_sink.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#if defined(ENABLE_TELEMETRY)
#include "backend/profiling/telemetry/telemetry.hpp"

namespace profiling {

inline void ignore_snprintf(int rc) noexcept { (void)rc; }

#if defined(ENABLE_TELEMETRY)
void logProfilerToConsole(const Telemetry &t) noexcept;
#endif

} // namespace profiling
#endif
Loading