Skip to content
Open
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
63 changes: 63 additions & 0 deletions source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,59 @@

#include <MaterialXFormat/Util.h>

#ifdef MATERIALX_BUILD_PERFETTO_TRACING
#include <MaterialXRenderGlsl/External/Glad/glad.h>
#include <chrono>
#endif

namespace mx = MaterialX;

#ifdef MATERIALX_BUILD_PERFETTO_TRACING
namespace {

uint64_t getCurrentTimeNs()
{
using namespace std::chrono;
return duration_cast<nanoseconds>(steady_clock::now().time_since_epoch()).count();
}

class GpuTimerQuery
{
public:
GpuTimerQuery()
{
glGenQueries(1, &_query);
}

~GpuTimerQuery()
{
glDeleteQueries(1, &_query);
}

void begin()
{
glBeginQuery(GL_TIME_ELAPSED, _query);
}

void end()
{
glEndQuery(GL_TIME_ELAPSED);
}

uint64_t getDurationNs()
{
GLuint64 elapsedTime;
glGetQueryObjectui64v(_query, GL_QUERY_RESULT, &elapsedTime);
return elapsedTime;
}

private:
GLuint _query;
};

} // anonymous namespace
#endif // MATERIALX_BUILD_PERFETTO_TRACING

//
// Render validation tester for the GLSL shading language
//
Expand Down Expand Up @@ -359,7 +410,19 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName,
unsigned int height = (unsigned int) testOptions.renderSize[1] * supersampleFactor;
_renderer->setSize(width, height);

#ifdef MATERIALX_BUILD_PERFETTO_TRACING
uint64_t cpuStartNs = getCurrentTimeNs();
GpuTimerQuery gpuTimer;
gpuTimer.begin();
#endif
_renderer->render();

#ifdef MATERIALX_BUILD_PERFETTO_TRACING
gpuTimer.end();
glFinish();
uint64_t gpuDurationNs = gpuTimer.getDurationNs();
MX_TRACE_ASYNC(mx::Tracing::AsyncTrack::GPU, mx::Tracing::Category::Render, shaderName.c_str(), cpuStartNs, gpuDurationNs);
#endif
}

{
Expand Down
42 changes: 42 additions & 0 deletions source/MaterialXTrace/PerfettoSink.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

#ifdef MATERIALX_BUILD_PERFETTO_TRACING

#include <cstdint>
#include <fstream>
#include <limits>
#include <mutex>

// Define Perfetto trace categories for MaterialX
Expand All @@ -31,6 +33,10 @@ MATERIALX_NAMESPACE_BEGIN
namespace Tracing
{

// Stable Perfetto track IDs for async tracks (must not collide with thread IDs).
// Use max uint64_t minus small offsets -- no OS will assign these as thread IDs.
constexpr uint64_t GPU_TRACK_ID = std::numeric_limits<uint64_t>::max();

PerfettoSink::PerfettoSink(std::string outputPath, size_t bufferSizeKb)
: _outputPath(std::move(outputPath))
{
Expand All @@ -41,6 +47,14 @@ PerfettoSink::PerfettoSink(std::string outputPath, size_t bufferSizeKb)
args.backends |= perfetto::kInProcessBackend;
perfetto::Tracing::Initialize(args);
perfetto::TrackEvent::Register();

// Initialize async track descriptors with stable IDs and names
{
perfetto::Track gpuTrack(GPU_TRACK_ID);
auto desc = gpuTrack.Serialize();
desc.set_name("GPU");
perfetto::TrackEvent::SetTrackDescriptor(gpuTrack, desc);
}
});

// Create and start a tracing session
Expand Down Expand Up @@ -155,6 +169,34 @@ void PerfettoSink::counter(Category category, const char* name, double value)
}
}

void PerfettoSink::asyncEvent(AsyncTrack track, Category category,
const char* eventName, uint64_t startNs, uint64_t durationNs)
{
// Currently only GPU track is supported
assert(track == AsyncTrack::GPU && "Only AsyncTrack::GPU is currently supported");
perfetto::Track perfTrack(GPU_TRACK_ID);

// Emit begin and end events with explicit timestamps
switch (category)
{
case Category::Render:
TRACE_EVENT_BEGIN("mx.render", nullptr, perfTrack, startNs,
[&](perfetto::EventContext ctx) { ctx.event()->set_name(eventName); });
TRACE_EVENT_END("mx.render", perfTrack, startNs + durationNs);
break;
case Category::ShaderGen:
TRACE_EVENT_BEGIN("mx.shadergen", nullptr, perfTrack, startNs,
[&](perfetto::EventContext ctx) { ctx.event()->set_name(eventName); });
TRACE_EVENT_END("mx.shadergen", perfTrack, startNs + durationNs);
break;
default:
TRACE_EVENT_BEGIN("mx.render", nullptr, perfTrack, startNs,
[&](perfetto::EventContext ctx) { ctx.event()->set_name(eventName); });
TRACE_EVENT_END("mx.render", perfTrack, startNs + durationNs);
break;
}
}

void PerfettoSink::setThreadName(const char* name)
{
// Set thread name for trace visualization
Expand Down
2 changes: 2 additions & 0 deletions source/MaterialXTrace/PerfettoSink.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ class PerfettoSink : public Sink
void beginEvent(Category category, const char* name) override;
void endEvent(Category category) override;
void counter(Category category, const char* name, double value) override;
void asyncEvent(AsyncTrack track, Category category,
const char* eventName, uint64_t startNs, uint64_t durationNs) override;
void setThreadName(const char* name) override;

private:
Expand Down
34 changes: 34 additions & 0 deletions source/MaterialXTrace/Tracing.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include <cassert>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>

Expand Down Expand Up @@ -55,6 +56,15 @@ enum class Category
Count
};

/// @enum AsyncTrack
/// Async track identifiers for operations with explicit timing (e.g., GPU work).
enum class AsyncTrack
{
/// GPU render operations (measured via GL timer queries)
GPU = 0
// Add more tracks here as needed (e.g., Compile, Transfer)
};

/// @class Sink
/// Abstract tracing sink interface.
///
Expand All @@ -74,6 +84,17 @@ class MX_TRACE_API Sink
/// Record a counter value (e.g., GPU time, memory usage).
virtual void counter(Category category, const char* name, double value) = 0;

/// Record an async event with explicit timing (e.g., GPU operations).
/// This creates a slice on a separate track, useful for GPU work that
/// runs asynchronously from CPU traces.
/// @param track The async track to record on (e.g., AsyncTrack::GPU)
/// @param category The trace category for filtering
/// @param eventName Name of the event (e.g., material name)
/// @param startNs Start timestamp in nanoseconds (can be approximate)
/// @param durationNs Duration in nanoseconds (should be accurate)
virtual void asyncEvent(AsyncTrack track, Category category,
const char* eventName, uint64_t startNs, uint64_t durationNs) = 0;

/// Set the current thread's name for trace visualization.
virtual void setThreadName(const char* name) = 0;
};
Expand Down Expand Up @@ -142,6 +163,14 @@ class MX_TRACE_API Dispatcher
_sink->counter(category, name, value);
}

/// Record an async event with explicit timing.
void asyncEvent(AsyncTrack track, Category category,
const char* eventName, uint64_t startNs, uint64_t durationNs)
{
if (_sink)
_sink->asyncEvent(track, category, eventName, startNs, durationNs);
}

private:
Dispatcher() = default;
Dispatcher(const Dispatcher&) = delete;
Expand Down Expand Up @@ -234,6 +263,10 @@ MATERIALX_NAMESPACE_END
#define MX_TRACE_COUNTER(category, name, value) \
MaterialX::Tracing::Dispatcher::getInstance().counter(category, name, value)

/// Record an async event with explicit timing (e.g., GPU operations).
#define MX_TRACE_ASYNC(track, category, eventName, startNs, durationNs) \
MaterialX::Tracing::Dispatcher::getInstance().asyncEvent(track, category, eventName, startNs, durationNs)

/// Begin a trace event (must be paired with MX_TRACE_END).
#define MX_TRACE_BEGIN(category, name) \
MaterialX::Tracing::Dispatcher::getInstance().beginEvent(category, name)
Expand All @@ -247,6 +280,7 @@ MATERIALX_NAMESPACE_END
#define MX_TRACE_SCOPE(category, name)
#define MX_TRACE_FUNCTION(category)
#define MX_TRACE_COUNTER(category, name, value)
#define MX_TRACE_ASYNC(track, category, eventName, startNs, durationNs)
#define MX_TRACE_BEGIN(category, name)
#define MX_TRACE_END(category)

Expand Down