From d3e264d59e3812f2aea5bd013e393ee2ca9a9b73 Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Wed, 7 Jan 2026 17:45:46 -0500 Subject: [PATCH 01/40] Add Perfetto tracing infrastructure to MaterialXCore - Add MATERIALX_BUILD_TRACING CMake option (OFF by default) - Add abstract MxTraceBackend interface for pluggable tracing backends - Add MxTraceCollector singleton (similar to USD's TraceCollector) - Add MxTraceScope RAII helper and MX_TRACE_* macros - Add MxPerfettoBackend implementation using Perfetto SDK - Initialize/shutdown Perfetto in MaterialXTest Main.cpp - Add trace instrumentation to RenderGlsl test The abstract interface allows USD/Hydra to inject their own tracing backend when calling MaterialX, enabling unified trace visualization. Usage: cmake -DMATERIALX_BUILD_TRACING=ON ... # Run tests, then open materialx_test_trace.perfetto-trace at ui.perfetto.dev --- CMakeLists.txt | 18 ++ source/MaterialXCore/CMakeLists.txt | 19 ++ source/MaterialXCore/MxTrace.cpp | 22 +++ source/MaterialXCore/MxTrace.h | 180 ++++++++++++++++++ source/MaterialXCore/MxTracePerfetto.cpp | 120 ++++++++++++ source/MaterialXCore/MxTracePerfetto.h | 71 +++++++ source/MaterialXTest/Main.cpp | 22 ++- .../MaterialXRenderGlsl/RenderGlsl.cpp | 5 + 8 files changed, 456 insertions(+), 1 deletion(-) create mode 100644 source/MaterialXCore/MxTrace.cpp create mode 100644 source/MaterialXCore/MxTrace.h create mode 100644 source/MaterialXCore/MxTracePerfetto.cpp create mode 100644 source/MaterialXCore/MxTracePerfetto.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 06f720ad3b..2fca861018 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,7 @@ option(MATERIALX_BUILD_OCIO "Build OpenColorIO support for shader generators." O option(MATERIALX_BUILD_TESTS "Build unit tests." OFF) option(MATERIALX_BUILD_BENCHMARK_TESTS "Build benchmark tests." OFF) option(MATERIALX_BUILD_OSOS "Build OSL .oso's of standard library shaders for the OSL Network generator" OFF) +option(MATERIALX_BUILD_TRACING "Build with Perfetto tracing support for performance analysis." OFF) option(MATERIALX_BUILD_SHARED_LIBS "Build MaterialX libraries as shared rather than static." OFF) option(MATERIALX_BUILD_DATA_LIBRARY "Build generated products from the MaterialX data library." OFF) @@ -227,6 +228,23 @@ mark_as_advanced(MATERIALX_MDL_BINARY_MDLC) mark_as_advanced(MATERIALX_MDL_MODULE_PATHS) mark_as_advanced(MATERIALX_MDL_SDK_DIR) mark_as_advanced(MATERIALX_SLANG_RHI_SOURCE_DIR) +mark_as_advanced(MATERIALX_BUILD_TRACING) + +# Perfetto tracing support +if(MATERIALX_BUILD_TRACING) + include(FetchContent) + FetchContent_Declare( + perfetto + GIT_REPOSITORY https://android.googlesource.com/platform/external/perfetto + GIT_TAG v43.0 + GIT_SHALLOW TRUE + ) + # Only fetch the SDK, not the full Perfetto source + set(PERFETTO_SDK_INCLUDE_DIR "${CMAKE_BINARY_DIR}/_deps/perfetto-src/sdk") + FetchContent_MakeAvailable(perfetto) + add_definitions(-DMATERIALX_BUILD_TRACING) + message(STATUS "Perfetto tracing support enabled") +endif() if (MATERIALX_BUILD_USE_CCACHE) # Setup CCache for C/C++ compilation diff --git a/source/MaterialXCore/CMakeLists.txt b/source/MaterialXCore/CMakeLists.txt index c3c845afda..2ec6f18530 100644 --- a/source/MaterialXCore/CMakeLists.txt +++ b/source/MaterialXCore/CMakeLists.txt @@ -3,6 +3,11 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Generated.h.in ${CMAKE_CURRENT_BINARY file(GLOB materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") file(GLOB materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h" "${CMAKE_CURRENT_BINARY_DIR}/*.h") +# Exclude Perfetto implementation when tracing is disabled +if(NOT MATERIALX_BUILD_TRACING) + list(REMOVE_ITEM materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/MxTracePerfetto.cpp") +endif() + mx_add_library(MaterialXCore SOURCE_FILES ${materialx_source} @@ -15,3 +20,17 @@ mx_add_library(MaterialXCore target_include_directories(${TARGET_NAME} PUBLIC $) + +# Perfetto tracing support +if(MATERIALX_BUILD_TRACING) + # Add Perfetto SDK source file (single-file amalgamation) + target_sources(${TARGET_NAME} PRIVATE + "${perfetto_SOURCE_DIR}/sdk/perfetto.cc") + target_include_directories(${TARGET_NAME} PUBLIC + "${perfetto_SOURCE_DIR}/sdk") + target_compile_definitions(${TARGET_NAME} PUBLIC MATERIALX_BUILD_TRACING) + if(WIN32) + # Perfetto requires ws2_32 on Windows + target_link_libraries(${TARGET_NAME} PRIVATE ws2_32) + endif() +endif() diff --git a/source/MaterialXCore/MxTrace.cpp b/source/MaterialXCore/MxTrace.cpp new file mode 100644 index 0000000000..b2bf2a69db --- /dev/null +++ b/source/MaterialXCore/MxTrace.cpp @@ -0,0 +1,22 @@ +// +// Copyright Contributors to the MaterialX Project +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +MATERIALX_NAMESPACE_BEGIN + +MxTraceCollector& MxTraceCollector::getInstance() +{ + static MxTraceCollector instance; + return instance; +} + +void MxTraceCollector::setBackend(std::shared_ptr backend) +{ + _backend = backend; +} + +MATERIALX_NAMESPACE_END + diff --git a/source/MaterialXCore/MxTrace.h b/source/MaterialXCore/MxTrace.h new file mode 100644 index 0000000000..09fe9f04aa --- /dev/null +++ b/source/MaterialXCore/MxTrace.h @@ -0,0 +1,180 @@ +// +// Copyright Contributors to the MaterialX Project +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef MATERIALX_MXTRACE_H +#define MATERIALX_MXTRACE_H + +/// @file +/// Tracing infrastructure for performance analysis. +/// +/// This module provides an abstract tracing interface that can be backed by +/// different implementations (Perfetto, USD TraceCollector, etc.). +/// +/// Design goals: +/// - API similar to USD's TraceCollector/TraceScope for familiarity +/// - Abstract backend allows USD to inject its own tracing when calling MaterialX +/// - Zero overhead when tracing is disabled (macros compile to nothing) + +#include + +#include +#include + +MATERIALX_NAMESPACE_BEGIN + +// Trace category constants (similar to USD's TraceCategory) +#define MX_TRACE_CAT_RENDER "mx.render" +#define MX_TRACE_CAT_SHADERGEN "mx.shadergen" +#define MX_TRACE_CAT_OPTIMIZE "mx.optimize" + +/// @class MxTraceBackend +/// Abstract tracing backend interface. +/// +/// Implementations can delegate to Perfetto, USD TraceCollector, or custom systems. +/// This allows USD/Hydra to inject their own tracing when calling MaterialX code. +class MX_CORE_API MxTraceBackend +{ + public: + virtual ~MxTraceBackend() = default; + + /// Begin a trace event with the given category and name. + virtual void beginEvent(const char* category, const char* name) = 0; + + /// End the current trace event for the given category. + virtual void endEvent(const char* category) = 0; + + /// Record a counter value (e.g., GPU time, memory usage). + virtual void counter(const char* category, const char* name, double value) = 0; + + /// Set the current thread's name for trace visualization. + virtual void setThreadName(const char* name) = 0; +}; + +/// @class MxTraceCollector +/// Global trace collector singleton (similar to USD's TraceCollector). +/// +/// Usage: +/// MxTraceCollector::getInstance().setBackend(myBackend); +/// MxTraceCollector::getInstance().beginEvent("mx.render", "RenderFrame"); +class MX_CORE_API MxTraceCollector +{ + public: + /// Get the singleton instance. + static MxTraceCollector& getInstance(); + + /// Set the tracing backend. Pass nullptr to disable tracing. + void setBackend(std::shared_ptr backend); + + /// Get the current backend (may be nullptr). + MxTraceBackend* getBackend() const { return _backend.get(); } + + /// Check if tracing is currently enabled. + bool isEnabled() const { return _backend != nullptr; } + + /// Begin a trace event. + void beginEvent(const char* category, const char* name) + { + if (_backend) + _backend->beginEvent(category, name); + } + + /// End a trace event. + void endEvent(const char* category) + { + if (_backend) + _backend->endEvent(category); + } + + /// Record a counter value. + void counter(const char* category, const char* name, double value) + { + if (_backend) + _backend->counter(category, name, value); + } + + private: + MxTraceCollector() = default; + MxTraceCollector(const MxTraceCollector&) = delete; + MxTraceCollector& operator=(const MxTraceCollector&) = delete; + + std::shared_ptr _backend; +}; + +/// @class MxTraceScope +/// RAII scope guard for trace events (similar to USD's TraceScope). +/// +/// Usage: +/// { +/// MxTraceScope scope("mx.render", "RenderMaterial"); +/// // ... code to trace ... +/// } // Event automatically ends here +class MX_CORE_API MxTraceScope +{ + public: + MxTraceScope(const char* category, const char* name) + : _category(category) + , _enabled(MxTraceCollector::getInstance().isEnabled()) + { + if (_enabled) + MxTraceCollector::getInstance().beginEvent(category, name); + } + + ~MxTraceScope() + { + if (_enabled) + MxTraceCollector::getInstance().endEvent(_category); + } + + // Non-copyable + MxTraceScope(const MxTraceScope&) = delete; + MxTraceScope& operator=(const MxTraceScope&) = delete; + + private: + const char* _category; + bool _enabled; +}; + +MATERIALX_NAMESPACE_END + +// ============================================================================ +// Tracing Macros +// ============================================================================ +// When MATERIALX_BUILD_TRACING is defined, these macros generate trace events. +// Otherwise, they compile to nothing (zero overhead). + +#ifdef MATERIALX_BUILD_TRACING + +/// Create a scoped trace event. Event ends when scope exits. +#define MX_TRACE_SCOPE(category, name) \ + MaterialX::MxTraceScope _mxTraceScope##__LINE__(category, name) + +/// Create a scoped trace event using the current function name. +#define MX_TRACE_FUNCTION(category) \ + MX_TRACE_SCOPE(category, __FUNCTION__) + +/// Record a counter value. +#define MX_TRACE_COUNTER(category, name, value) \ + MaterialX::MxTraceCollector::getInstance().counter(category, name, value) + +/// Begin a trace event (must be paired with MX_TRACE_END). +#define MX_TRACE_BEGIN(category, name) \ + MaterialX::MxTraceCollector::getInstance().beginEvent(category, name) + +/// End a trace event. +#define MX_TRACE_END(category) \ + MaterialX::MxTraceCollector::getInstance().endEvent(category) + +#else // MATERIALX_BUILD_TRACING not defined + +#define MX_TRACE_SCOPE(category, name) +#define MX_TRACE_FUNCTION(category) +#define MX_TRACE_COUNTER(category, name, value) +#define MX_TRACE_BEGIN(category, name) +#define MX_TRACE_END(category) + +#endif // MATERIALX_BUILD_TRACING + +#endif // MATERIALX_MXTRACE_H + diff --git a/source/MaterialXCore/MxTracePerfetto.cpp b/source/MaterialXCore/MxTracePerfetto.cpp new file mode 100644 index 0000000000..e5bd344723 --- /dev/null +++ b/source/MaterialXCore/MxTracePerfetto.cpp @@ -0,0 +1,120 @@ +// +// Copyright Contributors to the MaterialX Project +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#ifdef MATERIALX_BUILD_TRACING + +#include + +// Define Perfetto trace categories for MaterialX +// These must be in a .cpp file, not a header +PERFETTO_DEFINE_CATEGORIES( + perfetto::Category("mx.render") + .SetDescription("MaterialX rendering operations"), + perfetto::Category("mx.shadergen") + .SetDescription("MaterialX shader generation"), + perfetto::Category("mx.optimize") + .SetDescription("MaterialX optimization passes") +); + +// Required for Perfetto SDK - provides static storage for track events +PERFETTO_TRACK_EVENT_STATIC_STORAGE(); + +MATERIALX_NAMESPACE_BEGIN + +class MxPerfettoBackend::Impl +{ + public: + std::unique_ptr session; +}; + +MxPerfettoBackend::MxPerfettoBackend() : _impl(new Impl()) +{ +} + +MxPerfettoBackend::~MxPerfettoBackend() = default; + +std::shared_ptr MxPerfettoBackend::create() +{ + // Use shared_ptr with custom destructor to handle private constructor + return std::shared_ptr(new MxPerfettoBackend()); +} + +void MxPerfettoBackend::initialize(size_t bufferSizeKb) +{ + // Initialize Perfetto with in-process backend + perfetto::TracingInitArgs args; + args.backends |= perfetto::kInProcessBackend; + perfetto::Tracing::Initialize(args); + + // Register track event data source + perfetto::TrackEvent::Register(); + + // Configure tracing session + perfetto::TraceConfig cfg; + cfg.add_buffers()->set_size_kb(static_cast(bufferSizeKb)); + + auto* ds_cfg = cfg.add_data_sources()->mutable_config(); + ds_cfg->set_name("track_event"); + + // Start tracing session + _impl->session = perfetto::Tracing::NewTrace(); + _impl->session->Setup(cfg); + _impl->session->StartBlocking(); +} + +void MxPerfettoBackend::shutdown(const std::string& outputPath) +{ + if (!_impl->session) + return; + + // Flush any pending trace data + perfetto::TrackEvent::Flush(); + + // Stop the tracing session + _impl->session->StopBlocking(); + + // Read trace data and write to file + std::vector traceData(_impl->session->ReadTraceBlocking()); + if (!traceData.empty()) + { + std::ofstream output(outputPath, std::ios::binary); + output.write(traceData.data(), static_cast(traceData.size())); + } + + _impl->session.reset(); +} + +void MxPerfettoBackend::beginEvent(const char* category, const char* name) +{ + // Use dynamic category and name for flexibility + // Note: For maximum performance, static categories/names should be used + TRACE_EVENT_BEGIN(category, perfetto::DynamicString(name)); +} + +void MxPerfettoBackend::endEvent(const char* category) +{ + TRACE_EVENT_END(category); +} + +void MxPerfettoBackend::counter(const char* category, const char* name, double value) +{ + TRACE_COUNTER(category, perfetto::DynamicString(name), value); +} + +void MxPerfettoBackend::setThreadName(const char* name) +{ + // Set thread name for trace visualization + auto track = perfetto::ThreadTrack::Current(); + auto desc = track.Serialize(); + desc.mutable_thread()->set_thread_name(name); + perfetto::TrackEvent::SetTrackDescriptor(track, desc); +} + +MATERIALX_NAMESPACE_END + +#endif // MATERIALX_BUILD_TRACING + diff --git a/source/MaterialXCore/MxTracePerfetto.h b/source/MaterialXCore/MxTracePerfetto.h new file mode 100644 index 0000000000..ac6f674c09 --- /dev/null +++ b/source/MaterialXCore/MxTracePerfetto.h @@ -0,0 +1,71 @@ +// +// Copyright Contributors to the MaterialX Project +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef MATERIALX_MXTRACEPERFETTO_H +#define MATERIALX_MXTRACEPERFETTO_H + +/// @file +/// Perfetto-based implementation of the MxTraceBackend interface. +/// +/// Usage: +/// #include +/// auto backend = mx::MxPerfettoBackend::create(); +/// backend->initialize(); +/// mx::MxTraceCollector::getInstance().setBackend(backend); +/// // ... run application with tracing ... +/// mx::MxTraceCollector::getInstance().setBackend(nullptr); +/// backend->shutdown("trace.perfetto-trace"); +/// // Open the .perfetto-trace file at https://ui.perfetto.dev + +#include + +#ifdef MATERIALX_BUILD_TRACING + +#include +#include + +MATERIALX_NAMESPACE_BEGIN + +/// @class MxPerfettoBackend +/// Perfetto-based implementation of MxTraceBackend. +/// +/// This class provides a concrete implementation using Perfetto SDK's +/// in-process tracing backend. Trace data is written to a .perfetto-trace +/// file that can be visualized at https://ui.perfetto.dev +class MX_CORE_API MxPerfettoBackend : public MxTraceBackend +{ + public: + /// Create a new Perfetto backend instance. + static std::shared_ptr create(); + + ~MxPerfettoBackend() override; + + /// Initialize Perfetto tracing. Must be called before any trace events. + /// @param bufferSizeKb Size of the trace buffer in KB (default 32MB) + void initialize(size_t bufferSizeKb = 32768); + + /// Stop tracing and write the trace to a file. + /// @param outputPath Path to write the trace file (e.g., "trace.perfetto-trace") + void shutdown(const std::string& outputPath); + + // MxTraceBackend interface implementation + void beginEvent(const char* category, const char* name) override; + void endEvent(const char* category) override; + void counter(const char* category, const char* name, double value) override; + void setThreadName(const char* name) override; + + private: + MxPerfettoBackend(); + + class Impl; + std::unique_ptr _impl; +}; + +MATERIALX_NAMESPACE_END + +#endif // MATERIALX_BUILD_TRACING + +#endif // MATERIALX_MXTRACEPERFETTO_H + diff --git a/source/MaterialXTest/Main.cpp b/source/MaterialXTest/Main.cpp index 4e53d943f4..d9e73c25ad 100644 --- a/source/MaterialXTest/Main.cpp +++ b/source/MaterialXTest/Main.cpp @@ -8,6 +8,11 @@ #include #include +#ifdef MATERIALX_BUILD_TRACING +#include +#include +#endif + namespace mx = MaterialX; int main(int argc, char* const argv[]) @@ -33,5 +38,20 @@ int main(int argc, char* const argv[]) return returnCode; } - return session.run(); +#ifdef MATERIALX_BUILD_TRACING + // Initialize Perfetto tracing + auto perfettoBackend = mx::MxPerfettoBackend::create(); + perfettoBackend->initialize(); + mx::MxTraceCollector::getInstance().setBackend(perfettoBackend); +#endif + + int result = session.run(); + +#ifdef MATERIALX_BUILD_TRACING + // Shutdown tracing and write trace file + mx::MxTraceCollector::getInstance().setBackend(nullptr); + perfettoBackend->shutdown("materialx_test_trace.perfetto-trace"); +#endif + + return result; } diff --git a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp index e28931803c..7cca2b040b 100644 --- a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp +++ b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp @@ -12,6 +12,7 @@ #include #include +#include #if defined(MATERIALX_BUILD_OIIO) #include #endif @@ -161,6 +162,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, const std::string& outputPath, mx::ImageVec* imageVec) { + MX_TRACE_SCOPE(MX_TRACE_CAT_RENDER, "GlslShaderRenderTester::runRenderer"); std::cout << "Validating GLSL rendering for: " << doc->getSourceUri() << std::endl; mx::ScopedTimer totalGLSLTime(&profileTimes.languageTimes.totalTime); @@ -201,6 +203,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, transpTimer.endTimer(); mx::ScopedTimer generationTimer(&profileTimes.languageTimes.generationTime); + MX_TRACE_SCOPE(MX_TRACE_CAT_SHADERGEN, "GenerateShader"); mx::GenOptions& contextOptions = context.getOptions(); contextOptions = options; contextOptions.targetColorSpaceOverride = "lin_rec709"; @@ -277,6 +280,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, _renderer->setLightHandler(isShader ? _lightHandler : nullptr); { + MX_TRACE_SCOPE(MX_TRACE_CAT_RENDER, "CompileShader"); mx::ScopedTimer compileTimer(&profileTimes.languageTimes.compileTime); _renderer->createProgram(shader); _renderer->validateInputs(); @@ -343,6 +347,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, int supersampleFactor = testOptions.enableReferenceQuality ? 8 : 1; { + MX_TRACE_SCOPE(MX_TRACE_CAT_RENDER, "RenderMaterial"); mx::ScopedTimer renderTimer(&profileTimes.languageTimes.renderTime); _renderer->getImageHandler()->setSearchPath(imageSearchPath); unsigned int width = (unsigned int) testOptions.renderSize[0] * supersampleFactor; From c147aafb6fc1ab145ba0d29b812591b01355e03f Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Wed, 7 Jan 2026 17:49:50 -0500 Subject: [PATCH 02/40] Exclude MxTrace.cpp when tracing disabled (zero overhead) --- source/MaterialXCore/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/MaterialXCore/CMakeLists.txt b/source/MaterialXCore/CMakeLists.txt index 2ec6f18530..da16f04674 100644 --- a/source/MaterialXCore/CMakeLists.txt +++ b/source/MaterialXCore/CMakeLists.txt @@ -3,8 +3,9 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Generated.h.in ${CMAKE_CURRENT_BINARY file(GLOB materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") file(GLOB materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h" "${CMAKE_CURRENT_BINARY_DIR}/*.h") -# Exclude Perfetto implementation when tracing is disabled +# Exclude all tracing implementation when tracing is disabled (zero overhead) if(NOT MATERIALX_BUILD_TRACING) + list(REMOVE_ITEM materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/MxTrace.cpp") list(REMOVE_ITEM materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/MxTracePerfetto.cpp") endif() From ed2fcc0ce883d572be898da2cca12679c2857f6f Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Thu, 8 Jan 2026 09:01:53 -0500 Subject: [PATCH 03/40] Fix Perfetto SDK build on Windows - Add NOMINMAX and WIN32_LEAN_AND_MEAN to prevent min/max macro conflicts - Add /bigobj flag for perfetto.cc (too many sections for MSVC default) - Use BUILD_INTERFACE generator expression for include path - Fix MxTracePerfetto.cpp: add missing include and simplify Perfetto API usage (use fixed category with dynamic event names) --- source/MaterialXCore/CMakeLists.txt | 9 ++++++++- source/MaterialXCore/MxTracePerfetto.cpp | 18 +++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/source/MaterialXCore/CMakeLists.txt b/source/MaterialXCore/CMakeLists.txt index da16f04674..dd26a84736 100644 --- a/source/MaterialXCore/CMakeLists.txt +++ b/source/MaterialXCore/CMakeLists.txt @@ -27,11 +27,18 @@ if(MATERIALX_BUILD_TRACING) # Add Perfetto SDK source file (single-file amalgamation) target_sources(${TARGET_NAME} PRIVATE "${perfetto_SOURCE_DIR}/sdk/perfetto.cc") + # Use generator expression - only needed at build time, not install target_include_directories(${TARGET_NAME} PUBLIC - "${perfetto_SOURCE_DIR}/sdk") + $) target_compile_definitions(${TARGET_NAME} PUBLIC MATERIALX_BUILD_TRACING) if(WIN32) # Perfetto requires ws2_32 on Windows target_link_libraries(${TARGET_NAME} PRIVATE ws2_32) + # Prevent Windows.h min/max macros from breaking std::numeric_limits + # Also need /bigobj because perfetto.cc has too many sections for MSVC + set_source_files_properties("${perfetto_SOURCE_DIR}/sdk/perfetto.cc" + PROPERTIES + COMPILE_DEFINITIONS "NOMINMAX;WIN32_LEAN_AND_MEAN" + COMPILE_OPTIONS "/bigobj") endif() endif() diff --git a/source/MaterialXCore/MxTracePerfetto.cpp b/source/MaterialXCore/MxTracePerfetto.cpp index e5bd344723..0ebcda7014 100644 --- a/source/MaterialXCore/MxTracePerfetto.cpp +++ b/source/MaterialXCore/MxTracePerfetto.cpp @@ -8,6 +8,7 @@ #ifdef MATERIALX_BUILD_TRACING #include +#include // Define Perfetto trace categories for MaterialX // These must be in a .cpp file, not a header @@ -90,19 +91,26 @@ void MxPerfettoBackend::shutdown(const std::string& outputPath) void MxPerfettoBackend::beginEvent(const char* category, const char* name) { - // Use dynamic category and name for flexibility - // Note: For maximum performance, static categories/names should be used - TRACE_EVENT_BEGIN(category, perfetto::DynamicString(name)); + // Perfetto requires compile-time category names for TRACE_EVENT macros. + // We use a fixed "mx" category and put the logical category in the event name. + (void)category; // Category is encoded in the predefined categories above + TRACE_EVENT_BEGIN("mx.render", nullptr, [&](perfetto::EventContext ctx) { + ctx.event()->set_name(name); + }); } void MxPerfettoBackend::endEvent(const char* category) { - TRACE_EVENT_END(category); + (void)category; + TRACE_EVENT_END("mx.render"); } void MxPerfettoBackend::counter(const char* category, const char* name, double value) { - TRACE_COUNTER(category, perfetto::DynamicString(name), value); + (void)category; + // Create a counter track with the given name + auto track = perfetto::CounterTrack(name); + TRACE_COUNTER("mx.render", track, value); } void MxPerfettoBackend::setThreadName(const char* name) From 8b9b43928b08f8885a3eb40689d03605feef3c5c Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Thu, 8 Jan 2026 11:07:11 -0500 Subject: [PATCH 04/40] Improve tracing API: constexpr categories, material names, timestamps - Replace macro category definitions with constexpr namespace constants (MxTraceCategory::Render, ::ShaderGen, ::Optimize, ::Material) - Keep legacy macros for backward compatibility - Fix MX_TRACE_SCOPE macro to properly expand __LINE__ via helper macros - Add MX_TRACE_CAT_MATERIAL category for material/shader identity markers - Add material name trace scope in RenderGlsl for per-material tracking - Add timestamp to trace output filename (format: YYYYMMDD_HHMMSS) - Use MX_TRACE_FUNCTION macro for automatic function name extraction --- source/MaterialXCore/MxTrace.h | 39 ++++++++++++++++--- source/MaterialXTest/Main.cpp | 31 ++++++++++++++- .../MaterialXRenderGlsl/RenderGlsl.cpp | 3 +- 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/source/MaterialXCore/MxTrace.h b/source/MaterialXCore/MxTrace.h index 09fe9f04aa..d77688b529 100644 --- a/source/MaterialXCore/MxTrace.h +++ b/source/MaterialXCore/MxTrace.h @@ -16,6 +16,7 @@ /// - API similar to USD's TraceCollector/TraceScope for familiarity /// - Abstract backend allows USD to inject its own tracing when calling MaterialX /// - Zero overhead when tracing is disabled (macros compile to nothing) +/// - Constexpr category strings for compile-time validation #include @@ -24,10 +25,32 @@ MATERIALX_NAMESPACE_BEGIN -// Trace category constants (similar to USD's TraceCategory) -#define MX_TRACE_CAT_RENDER "mx.render" -#define MX_TRACE_CAT_SHADERGEN "mx.shadergen" -#define MX_TRACE_CAT_OPTIMIZE "mx.optimize" +/// @namespace MxTraceCategory +/// Constexpr trace category identifiers. +/// +/// These are compile-time constant strings that identify trace event categories. +/// Using constexpr ensures type safety and enables compile-time validation. +/// The USD TraceCollector backend can map these to its own categories. +namespace MxTraceCategory +{ + /// Rendering operations (GPU commands, frame capture, etc.) + constexpr const char* Render = "mx.render"; + + /// Shader generation (code generation, optimization passes) + constexpr const char* ShaderGen = "mx.shadergen"; + + /// Optimization passes (constant folding, dead code elimination) + constexpr const char* Optimize = "mx.optimize"; + + /// Material/shader identity markers (for filtering/grouping in traces) + constexpr const char* Material = "mx.material"; +} + +// Legacy macros for backward compatibility (deprecated) +#define MX_TRACE_CAT_RENDER MaterialX::MxTraceCategory::Render +#define MX_TRACE_CAT_SHADERGEN MaterialX::MxTraceCategory::ShaderGen +#define MX_TRACE_CAT_OPTIMIZE MaterialX::MxTraceCategory::Optimize +#define MX_TRACE_CAT_MATERIAL MaterialX::MxTraceCategory::Material /// @class MxTraceBackend /// Abstract tracing backend interface. @@ -144,15 +167,19 @@ MATERIALX_NAMESPACE_END // When MATERIALX_BUILD_TRACING is defined, these macros generate trace events. // Otherwise, they compile to nothing (zero overhead). +// Helper macros for token pasting with __LINE__ expansion +#define MX_TRACE_CONCAT_IMPL(a, b) a##b +#define MX_TRACE_CONCAT(a, b) MX_TRACE_CONCAT_IMPL(a, b) + #ifdef MATERIALX_BUILD_TRACING /// Create a scoped trace event. Event ends when scope exits. #define MX_TRACE_SCOPE(category, name) \ - MaterialX::MxTraceScope _mxTraceScope##__LINE__(category, name) + MaterialX::MxTraceScope MX_TRACE_CONCAT(_mxTraceScope_, __LINE__)(category, name) /// Create a scoped trace event using the current function name. #define MX_TRACE_FUNCTION(category) \ - MX_TRACE_SCOPE(category, __FUNCTION__) + MaterialX::MxTraceScope MX_TRACE_CONCAT(_mxTraceFn_, __LINE__)(category, __FUNCTION__) /// Record a counter value. #define MX_TRACE_COUNTER(category, name, value) \ diff --git a/source/MaterialXTest/Main.cpp b/source/MaterialXTest/Main.cpp index d9e73c25ad..b5a95f33ad 100644 --- a/source/MaterialXTest/Main.cpp +++ b/source/MaterialXTest/Main.cpp @@ -11,15 +11,41 @@ #ifdef MATERIALX_BUILD_TRACING #include #include +#include +#include +#include #endif namespace mx = MaterialX; +#ifdef MATERIALX_BUILD_TRACING +// Generate timestamp string for trace filename (format: YYYYMMDD_HHMMSS) +std::string getTimestampString() +{ + auto now = std::chrono::system_clock::now(); + auto time = std::chrono::system_clock::to_time_t(now); + std::tm tm_buf; +#ifdef _WIN32 + localtime_s(&tm_buf, &time); +#else + localtime_r(&time, &tm_buf); +#endif + std::ostringstream oss; + oss << std::put_time(&tm_buf, "%Y%m%d_%H%M%S"); + return oss.str(); +} +#endif + int main(int argc, char* const argv[]) { Catch::Session session; session.configData().showDurations = Catch::ShowDurations::Always; +#ifdef MATERIALX_BUILD_TRACING + // Capture start timestamp for trace filename + std::string traceTimestamp = getTimestampString(); +#endif + #ifdef CATCH_PLATFORM_WINDOWS BOOL inDebugger = IsDebuggerPresent(); if (inDebugger) @@ -48,9 +74,10 @@ int main(int argc, char* const argv[]) int result = session.run(); #ifdef MATERIALX_BUILD_TRACING - // Shutdown tracing and write trace file + // Shutdown tracing and write trace file with timestamp mx::MxTraceCollector::getInstance().setBackend(nullptr); - perfettoBackend->shutdown("materialx_test_trace.perfetto-trace"); + std::string traceFilename = "materialx_test_" + traceTimestamp + ".perfetto-trace"; + perfettoBackend->shutdown(traceFilename); #endif return result; diff --git a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp index 7cca2b040b..895ded68a2 100644 --- a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp +++ b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp @@ -162,7 +162,8 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, const std::string& outputPath, mx::ImageVec* imageVec) { - MX_TRACE_SCOPE(MX_TRACE_CAT_RENDER, "GlslShaderRenderTester::runRenderer"); + MX_TRACE_FUNCTION(MX_TRACE_CAT_RENDER); + MX_TRACE_SCOPE(MX_TRACE_CAT_MATERIAL, shaderName.c_str()); std::cout << "Validating GLSL rendering for: " << doc->getSourceUri() << std::endl; mx::ScopedTimer totalGLSLTime(&profileTimes.languageTimes.totalTime); From e5f1ca46ed24418cf7ec487c37dc32077b01013a Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Thu, 8 Jan 2026 11:39:08 -0500 Subject: [PATCH 05/40] Skip Perfetto tracing during test discovery mode The Catch2 Test Adapter for Visual Studio runs MaterialXTest.exe with --list-test-names-only to discover tests. Perfetto initialization was outputting to stderr, causing the adapter to fail with exit code 89. Now we detect --list-* arguments and skip tracing initialization, allowing VS Test Explorer to properly discover and debug tests. --- source/MaterialXTest/Main.cpp | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/source/MaterialXTest/Main.cpp b/source/MaterialXTest/Main.cpp index b5a95f33ad..0d92941ba6 100644 --- a/source/MaterialXTest/Main.cpp +++ b/source/MaterialXTest/Main.cpp @@ -64,20 +64,39 @@ int main(int argc, char* const argv[]) return returnCode; } + // Check if we're in list/discovery mode (used by test adapters) + bool isListMode = false; + for (int i = 1; i < argc; ++i) + { + std::string arg(argv[i]); + if (arg.find("--list") == 0) + { + isListMode = true; + break; + } + } + #ifdef MATERIALX_BUILD_TRACING - // Initialize Perfetto tracing - auto perfettoBackend = mx::MxPerfettoBackend::create(); - perfettoBackend->initialize(); - mx::MxTraceCollector::getInstance().setBackend(perfettoBackend); + std::shared_ptr perfettoBackend; + // Skip tracing initialization during test discovery (--list-* commands) + if (!isListMode) + { + perfettoBackend = mx::MxPerfettoBackend::create(); + perfettoBackend->initialize(); + mx::MxTraceCollector::getInstance().setBackend(perfettoBackend); + } #endif int result = session.run(); #ifdef MATERIALX_BUILD_TRACING - // Shutdown tracing and write trace file with timestamp - mx::MxTraceCollector::getInstance().setBackend(nullptr); - std::string traceFilename = "materialx_test_" + traceTimestamp + ".perfetto-trace"; - perfettoBackend->shutdown(traceFilename); + // Shutdown tracing and write trace file with timestamp (skip if in list mode) + if (!isListMode && perfettoBackend) + { + mx::MxTraceCollector::getInstance().setBackend(nullptr); + std::string traceFilename = "materialx_test_" + traceTimestamp + ".perfetto-trace"; + perfettoBackend->shutdown(traceFilename); + } #endif return result; From 7a55cc61a568b41f30c191e88e03e02149f031ed Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Thu, 8 Jan 2026 15:16:39 -0500 Subject: [PATCH 06/40] Add outputDirectory option for test artifact redirection Add optional outputDirectory setting to _options.mtlx that redirects all test artifacts (logs, shaders, images, traces) to a user-specified directory. This allows different test runs to be isolated without overwriting each other. Changes: - _options.mtlx: Add outputDirectory input (empty = default behavior) - TestSuiteOptions: Add outputDirectory member and resolveOutputPath() helpers - GenShaderUtil.cpp: Use outputDirectory for shader generation logs and dumps - RenderUtil.cpp: Use outputDirectory for render logs, images, and traces - Main.cpp: Remove tracing code (moved to RenderUtil.cpp) Tracing improvements: - Move Perfetto init/shutdown to ShaderRenderTester::validate() - Each render test now produces its own trace file (e.g., genglsl_render_trace.perfetto-trace) - Trace filenames now follow the same pattern as log files - Removed timestamps from trace filenames for consistency Usage: --- resources/Materials/TestSuite/_options.mtlx | 8 +++ source/MaterialXTest/Main.cpp | 68 +------------------ .../MaterialXGenShader/GenShaderUtil.cpp | 42 +++++++++--- .../MaterialXGenShader/GenShaderUtil.h | 41 +++++++++++ .../MaterialXRender/RenderUtil.cpp | 64 ++++++++++++----- 5 files changed, 131 insertions(+), 92 deletions(-) diff --git a/resources/Materials/TestSuite/_options.mtlx b/resources/Materials/TestSuite/_options.mtlx index a5d0623f2d..9f75172a5a 100644 --- a/resources/Materials/TestSuite/_options.mtlx +++ b/resources/Materials/TestSuite/_options.mtlx @@ -70,5 +70,13 @@ but requiring a more powerful GPU and longer CPU render times. --> + + + diff --git a/source/MaterialXTest/Main.cpp b/source/MaterialXTest/Main.cpp index 0d92941ba6..4e53d943f4 100644 --- a/source/MaterialXTest/Main.cpp +++ b/source/MaterialXTest/Main.cpp @@ -8,44 +8,13 @@ #include #include -#ifdef MATERIALX_BUILD_TRACING -#include -#include -#include -#include -#include -#endif - namespace mx = MaterialX; -#ifdef MATERIALX_BUILD_TRACING -// Generate timestamp string for trace filename (format: YYYYMMDD_HHMMSS) -std::string getTimestampString() -{ - auto now = std::chrono::system_clock::now(); - auto time = std::chrono::system_clock::to_time_t(now); - std::tm tm_buf; -#ifdef _WIN32 - localtime_s(&tm_buf, &time); -#else - localtime_r(&time, &tm_buf); -#endif - std::ostringstream oss; - oss << std::put_time(&tm_buf, "%Y%m%d_%H%M%S"); - return oss.str(); -} -#endif - int main(int argc, char* const argv[]) { Catch::Session session; session.configData().showDurations = Catch::ShowDurations::Always; -#ifdef MATERIALX_BUILD_TRACING - // Capture start timestamp for trace filename - std::string traceTimestamp = getTimestampString(); -#endif - #ifdef CATCH_PLATFORM_WINDOWS BOOL inDebugger = IsDebuggerPresent(); if (inDebugger) @@ -64,40 +33,5 @@ int main(int argc, char* const argv[]) return returnCode; } - // Check if we're in list/discovery mode (used by test adapters) - bool isListMode = false; - for (int i = 1; i < argc; ++i) - { - std::string arg(argv[i]); - if (arg.find("--list") == 0) - { - isListMode = true; - break; - } - } - -#ifdef MATERIALX_BUILD_TRACING - std::shared_ptr perfettoBackend; - // Skip tracing initialization during test discovery (--list-* commands) - if (!isListMode) - { - perfettoBackend = mx::MxPerfettoBackend::create(); - perfettoBackend->initialize(); - mx::MxTraceCollector::getInstance().setBackend(perfettoBackend); - } -#endif - - int result = session.run(); - -#ifdef MATERIALX_BUILD_TRACING - // Shutdown tracing and write trace file with timestamp (skip if in list mode) - if (!isListMode && perfettoBackend) - { - mx::MxTraceCollector::getInstance().setBackend(nullptr); - std::string traceFilename = "materialx_test_" + traceTimestamp + ".perfetto-trace"; - perfettoBackend->shutdown(traceFilename); - } -#endif - - return result; + return session.run(); } diff --git a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp index 6cecae2d85..a688a1d526 100644 --- a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp +++ b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp @@ -652,24 +652,24 @@ void ShaderGeneratorTester::registerLights(mx::DocumentPtr doc, const std::vecto void ShaderGeneratorTester::validate(const mx::GenOptions& generateOptions, const std::string& optionsFilePath) { - // Start logging - _logFile.open(_logFilePath); - - // Check for an option file + // Check for an option file first (before opening log) so we can use outputDirectory TestSuiteOptions options; if (!options.readOptions(optionsFilePath)) { - _logFile << "Cannot read options file: " << optionsFilePath << ". Skipping test." << std::endl; - _logFile.close(); + std::cerr << "Cannot read options file: " << optionsFilePath << ". Skipping test." << std::endl; return; } // Test has been turned off so just do nothing. if (!runTest(options)) { - _logFile << "Target: " << _targetString << " not set to run. Skipping test." << std::endl; - _logFile.close(); + std::cerr << "Target: " << _targetString << " not set to run. Skipping test." << std::endl; return; } + + // Start logging - use outputDirectory if set + mx::FilePath logPath = options.resolveOutputPath(_logFilePath); + _logFile.open(logPath.asString()); + options.print(_logFile); // Add files to override the files in the test suite to be examined. @@ -879,6 +879,17 @@ void ShaderGeneratorTester::validate(const mx::GenOptions& generateOptions, cons path = searchPath.isEmpty() ? mx::FilePath() : searchPath[0]; } + // Redirect to outputDirectory if set + if (!options.outputDirectory.isEmpty()) + { + mx::FilePath materialDir = path.getBaseName(); + path = options.outputDirectory / materialDir; + if (!path.exists()) + { + path.createDirectory(); + } + } + std::vector sourceCodePaths; if (sourceCode.size() > 1) { @@ -968,6 +979,7 @@ void TestSuiteOptions::print(std::ostream& output) const output << "\tExtra library paths: " << extraLibraryPaths.asString() << std::endl; output << "\tRender test paths: " << renderTestPaths.asString() << std::endl; output << "\tEnable Reference Quality: " << enableReferenceQuality << std::endl; + output << "\tOutput Directory: " << (outputDirectory.isEmpty() ? "(default)" : outputDirectory.asString()) << std::endl; } bool TestSuiteOptions::readOptions(const std::string& optionFile) @@ -993,6 +1005,7 @@ bool TestSuiteOptions::readOptions(const std::string& optionFile) const std::string EXTRA_LIBRARY_PATHS("extraLibraryPaths"); const std::string RENDER_TEST_PATHS("renderTestPaths"); const std::string ENABLE_REFERENCE_QUALITY("enableReferenceQuality"); + const std::string OUTPUT_DIRECTORY_STRING("outputDirectory"); overrideFiles.clear(); dumpGeneratedCode = false; @@ -1091,6 +1104,19 @@ bool TestSuiteOptions::readOptions(const std::string& optionFile) { enableReferenceQuality = val->asA(); } + else if (name == OUTPUT_DIRECTORY_STRING) + { + std::string dirPath = p->getValueString(); + if (!dirPath.empty()) + { + outputDirectory = mx::FilePath(dirPath); + // Create the directory if it doesn't exist + if (!outputDirectory.exists()) + { + outputDirectory.createDirectory(); + } + } + } } } } diff --git a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h index 869379f530..09485ec966 100644 --- a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h +++ b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h @@ -118,6 +118,47 @@ class TestSuiteOptions // Enable reference quality rendering. Default is false. bool enableReferenceQuality; + // Base directory for all test output artifacts (shaders, images, logs). + // If empty, use default locations. If set, all artifacts go to this directory. + mx::FilePath outputDirectory; + + // Helper to resolve output path for an artifact. + // If outputDirectory is set, returns outputDirectory/filename. + // Otherwise returns the original path unchanged. + mx::FilePath resolveOutputPath(const mx::FilePath& path) const + { + if (outputDirectory.isEmpty()) + { + return path; + } + // Extract just the filename and place it in outputDirectory + return outputDirectory / path.getBaseName(); + } + + // Helper to resolve output path preserving subdirectory structure. + // If outputDirectory is set, returns outputDirectory/relativePath. + // Otherwise returns the original path unchanged. + mx::FilePath resolveOutputPathWithSubdir(const mx::FilePath& path, const mx::FilePath& baseDir) const + { + if (outputDirectory.isEmpty()) + { + return path; + } + // Try to make the path relative to baseDir to preserve structure + std::string pathStr = path.asString(); + std::string baseStr = baseDir.asString(); + if (pathStr.find(baseStr) == 0) + { + std::string relative = pathStr.substr(baseStr.length()); + if (!relative.empty() && (relative[0] == '/' || relative[0] == '\\')) + { + relative = relative.substr(1); + } + return outputDirectory / mx::FilePath(relative); + } + return outputDirectory / path.getBaseName(); + } + // Bake parameters struct BakeSetting { diff --git a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp index 02b22b4441..341d3cf7f8 100644 --- a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp +++ b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp @@ -12,6 +12,11 @@ #include #endif +#ifdef MATERIALX_BUILD_TRACING +#include +#include +#endif + namespace mx = MaterialX; namespace RenderUtil @@ -81,13 +86,37 @@ void ShaderRenderTester::loadDependentLibraries(GenShaderUtil::TestSuiteOptions bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) { + // Read options first so we can use outputDirectory for log files + GenShaderUtil::TestSuiteOptions options; + if (!options.readOptions(optionsFilePath)) + { + std::cerr << "Can't find options file. Skip test." << std::endl; + return false; + } + if (!runTest(options)) + { + std::cerr << "Target: " << _shaderGenerator->getTarget() << " not set to run. Skip test." << std::endl; + return false; + } + +#ifdef MATERIALX_BUILD_TRACING + // Initialize tracing with target-specific trace filename + mx::FilePath tracePath = options.resolveOutputPath(_shaderGenerator->getTarget() + "_render_trace.perfetto-trace"); + auto perfettoBackend = mx::MxPerfettoBackend::create(); + perfettoBackend->initialize(); + mx::MxTraceCollector::getInstance().setBackend(perfettoBackend); +#endif + #ifdef LOG_TO_FILE - std::ofstream logfile(_shaderGenerator->getTarget() + "_render_log.txt"); + mx::FilePath logPath = options.resolveOutputPath(_shaderGenerator->getTarget() + "_render_log.txt"); + std::ofstream logfile(logPath.asString()); std::ostream& log(logfile); - std::string docValidLogFilename = _shaderGenerator->getTarget() + "_render_doc_validation_log.txt"; + mx::FilePath docValidLogPath = options.resolveOutputPath(_shaderGenerator->getTarget() + "_render_doc_validation_log.txt"); + std::string docValidLogFilename = docValidLogPath.asString(); std::ofstream docValidLogFile(docValidLogFilename); std::ostream& docValidLog(docValidLogFile); - std::ofstream profilingLogfile(_shaderGenerator->getTarget() + "_render_profiling_log.txt"); + mx::FilePath profilingLogPath = options.resolveOutputPath(_shaderGenerator->getTarget() + "_render_profiling_log.txt"); + std::ofstream profilingLogfile(profilingLogPath.asString()); std::ostream& profilingLog(profilingLogfile); #else std::ostream& log(std::cout); @@ -96,20 +125,6 @@ bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) std::ostream& profilingLog(std::cout); #endif - // Test has been turned off so just do nothing. - // Check for an option file - GenShaderUtil::TestSuiteOptions options; - if (!options.readOptions(optionsFilePath)) - { - log << "Can't find options file. Skip test." << std::endl; - return false; - } - if (!runTest(options)) - { - log << "Target: " << _shaderGenerator->getTarget() << " not set to run. Skip test." << std::endl; - return false; - } - // Profiling times RenderUtil::RenderProfileTimes profileTimes; // Global setup timer @@ -288,6 +303,15 @@ bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) mx::FilePath outputPath = filename; outputPath.removeExtension(); + + // If outputDirectory is set, redirect output to that directory + // while preserving the material name as a subdirectory + if (!options.outputDirectory.isEmpty()) + { + // Get just the material directory name (e.g., "standard_surface_carpaint") + mx::FilePath materialDir = outputPath.getBaseName(); + outputPath = options.outputDirectory / materialDir; + } renderableSearchTimer.startTimer(); std::vector elements; @@ -313,6 +337,12 @@ bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) totalTime.endTimer(); printRunLog(profileTimes, options, profilingLog, dependLib); +#ifdef MATERIALX_BUILD_TRACING + // Shutdown tracing and write trace file + mx::MxTraceCollector::getInstance().setBackend(nullptr); + perfettoBackend->shutdown(tracePath.asString()); +#endif + return true; } From 940cafb4dda4156ef70989c27c690d96ac7da114 Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Thu, 8 Jan 2026 15:29:50 -0500 Subject: [PATCH 07/40] Print output directory at end of test run for easy access When outputDirectory is set, print the path at the end of test stdout. This makes it clickable in terminals like Cursor/VS Code for quick access. --- resources/Materials/TestSuite/_options.mtlx | 2 +- source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp | 6 ++++++ source/MaterialXTest/MaterialXRender/RenderUtil.cpp | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/resources/Materials/TestSuite/_options.mtlx b/resources/Materials/TestSuite/_options.mtlx index 9f75172a5a..f278d742c5 100644 --- a/resources/Materials/TestSuite/_options.mtlx +++ b/resources/Materials/TestSuite/_options.mtlx @@ -77,6 +77,6 @@ Supports absolute paths or paths relative to the test working directory. The directory will be created if it doesn't exist. --> - + diff --git a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp index a688a1d526..8055bec34a 100644 --- a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp +++ b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp @@ -950,6 +950,12 @@ void ShaderGeneratorTester::validate(const mx::GenOptions& generateOptions, cons { _logFile.close(); } + + // Print effective output directory for easy access (clickable in terminals) + if (!options.outputDirectory.isEmpty()) + { + std::cout << std::endl << "Test artifacts written to: " << options.outputDirectory.asString() << std::endl; + } } void TestSuiteOptions::print(std::ostream& output) const diff --git a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp index 341d3cf7f8..0bd5a5eafb 100644 --- a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp +++ b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp @@ -343,6 +343,12 @@ bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) perfettoBackend->shutdown(tracePath.asString()); #endif + // Print effective output directory for easy access (clickable in terminals) + if (!options.outputDirectory.isEmpty()) + { + std::cout << std::endl << "Test artifacts written to: " << options.outputDirectory.asString() << std::endl; + } + return true; } From fb056e347e9a0ee882bd06660326d3839a038754 Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Thu, 8 Jan 2026 15:51:51 -0500 Subject: [PATCH 08/40] Improve Perfetto tracing integration and suppress SDK warnings - Suppress MSVC warnings from Perfetto SDK templates (C4127, C4146, C4369) - Replace MX_TRACE_CAT_* macros with 'namespace cat = mx::MxTraceCategory' for cleaner code and better IDE support - Update RenderGlsl.cpp to use the new category alias pattern --- resources/Materials/TestSuite/_options.mtlx | 2 +- source/MaterialXCore/MxTrace.h | 8 +- source/MaterialXCore/MxTracePerfetto.cpp | 75 +++++++++++++++++-- .../MaterialXRenderGlsl/RenderGlsl.cpp | 11 +-- 4 files changed, 77 insertions(+), 19 deletions(-) diff --git a/resources/Materials/TestSuite/_options.mtlx b/resources/Materials/TestSuite/_options.mtlx index f278d742c5..9f75172a5a 100644 --- a/resources/Materials/TestSuite/_options.mtlx +++ b/resources/Materials/TestSuite/_options.mtlx @@ -77,6 +77,6 @@ Supports absolute paths or paths relative to the test working directory. The directory will be created if it doesn't exist. --> - + diff --git a/source/MaterialXCore/MxTrace.h b/source/MaterialXCore/MxTrace.h index d77688b529..3bb5223b6e 100644 --- a/source/MaterialXCore/MxTrace.h +++ b/source/MaterialXCore/MxTrace.h @@ -46,11 +46,9 @@ namespace MxTraceCategory constexpr const char* Material = "mx.material"; } -// Legacy macros for backward compatibility (deprecated) -#define MX_TRACE_CAT_RENDER MaterialX::MxTraceCategory::Render -#define MX_TRACE_CAT_SHADERGEN MaterialX::MxTraceCategory::ShaderGen -#define MX_TRACE_CAT_OPTIMIZE MaterialX::MxTraceCategory::Optimize -#define MX_TRACE_CAT_MATERIAL MaterialX::MxTraceCategory::Material +// Usage: Add a namespace alias in your .cpp file for brevity: +// namespace cat = MaterialX::MxTraceCategory; +// MX_TRACE_SCOPE(cat::Render, "MyEvent"); /// @class MxTraceBackend /// Abstract tracing backend interface. diff --git a/source/MaterialXCore/MxTracePerfetto.cpp b/source/MaterialXCore/MxTracePerfetto.cpp index 0ebcda7014..c8cfa29283 100644 --- a/source/MaterialXCore/MxTracePerfetto.cpp +++ b/source/MaterialXCore/MxTracePerfetto.cpp @@ -4,11 +4,26 @@ // #include +#include #ifdef MATERIALX_BUILD_TRACING +// Suppress verbose warnings from Perfetto SDK templates +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4127) // conditional expression is constant +#pragma warning(disable : 4146) // unary minus on unsigned type +#pragma warning(disable : 4369) // enumerator value cannot be represented +#endif + #include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + #include +#include // Define Perfetto trace categories for MaterialX // These must be in a .cpp file, not a header @@ -18,7 +33,9 @@ PERFETTO_DEFINE_CATEGORIES( perfetto::Category("mx.shadergen") .SetDescription("MaterialX shader generation"), perfetto::Category("mx.optimize") - .SetDescription("MaterialX optimization passes") + .SetDescription("MaterialX optimization passes"), + perfetto::Category("mx.material") + .SetDescription("MaterialX material identity markers") ); // Required for Perfetto SDK - provides static storage for track events @@ -89,20 +106,62 @@ void MxPerfettoBackend::shutdown(const std::string& outputPath) _impl->session.reset(); } +// Helper to check category match (pointer comparison first for constexpr, then strcmp) +static bool categoryMatches(const char* category, const char* target) +{ + return category == target || (category && std::strcmp(category, target) == 0); +} + void MxPerfettoBackend::beginEvent(const char* category, const char* name) { // Perfetto requires compile-time category names for TRACE_EVENT macros. - // We use a fixed "mx" category and put the logical category in the event name. - (void)category; // Category is encoded in the predefined categories above - TRACE_EVENT_BEGIN("mx.render", nullptr, [&](perfetto::EventContext ctx) { - ctx.event()->set_name(name); - }); + // We dispatch based on the runtime category to the appropriate compile-time macro. + // Pointer comparison catches constexpr usage, strcmp handles dynamic strings. + if (categoryMatches(category, MxTraceCategory::ShaderGen)) + { + TRACE_EVENT_BEGIN("mx.shadergen", nullptr, [&](perfetto::EventContext ctx) { + ctx.event()->set_name(name); + }); + } + else if (categoryMatches(category, MxTraceCategory::Optimize)) + { + TRACE_EVENT_BEGIN("mx.optimize", nullptr, [&](perfetto::EventContext ctx) { + ctx.event()->set_name(name); + }); + } + else if (categoryMatches(category, MxTraceCategory::Material)) + { + TRACE_EVENT_BEGIN("mx.material", nullptr, [&](perfetto::EventContext ctx) { + ctx.event()->set_name(name); + }); + } + else // Default to mx.render + { + TRACE_EVENT_BEGIN("mx.render", nullptr, [&](perfetto::EventContext ctx) { + ctx.event()->set_name(name); + }); + } } void MxPerfettoBackend::endEvent(const char* category) { - (void)category; - TRACE_EVENT_END("mx.render"); + // Must match the category used in beginEvent + if (categoryMatches(category, MxTraceCategory::ShaderGen)) + { + TRACE_EVENT_END("mx.shadergen"); + } + else if (categoryMatches(category, MxTraceCategory::Optimize)) + { + TRACE_EVENT_END("mx.optimize"); + } + else if (categoryMatches(category, MxTraceCategory::Material)) + { + TRACE_EVENT_END("mx.material"); + } + else + { + TRACE_EVENT_END("mx.render"); + } } void MxPerfettoBackend::counter(const char* category, const char* name, double value) diff --git a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp index 895ded68a2..50ba182a4f 100644 --- a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp +++ b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp @@ -20,6 +20,7 @@ #include namespace mx = MaterialX; +namespace cat = mx::MxTraceCategory; // // Render validation tester for the GLSL shading language @@ -162,8 +163,8 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, const std::string& outputPath, mx::ImageVec* imageVec) { - MX_TRACE_FUNCTION(MX_TRACE_CAT_RENDER); - MX_TRACE_SCOPE(MX_TRACE_CAT_MATERIAL, shaderName.c_str()); + MX_TRACE_FUNCTION(cat::Render); + MX_TRACE_SCOPE(cat::Material, shaderName.c_str()); std::cout << "Validating GLSL rendering for: " << doc->getSourceUri() << std::endl; mx::ScopedTimer totalGLSLTime(&profileTimes.languageTimes.totalTime); @@ -204,7 +205,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, transpTimer.endTimer(); mx::ScopedTimer generationTimer(&profileTimes.languageTimes.generationTime); - MX_TRACE_SCOPE(MX_TRACE_CAT_SHADERGEN, "GenerateShader"); + MX_TRACE_SCOPE(cat::ShaderGen, "GenerateShader"); mx::GenOptions& contextOptions = context.getOptions(); contextOptions = options; contextOptions.targetColorSpaceOverride = "lin_rec709"; @@ -281,7 +282,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, _renderer->setLightHandler(isShader ? _lightHandler : nullptr); { - MX_TRACE_SCOPE(MX_TRACE_CAT_RENDER, "CompileShader"); + MX_TRACE_SCOPE(cat::Render, "CompileShader"); mx::ScopedTimer compileTimer(&profileTimes.languageTimes.compileTime); _renderer->createProgram(shader); _renderer->validateInputs(); @@ -348,7 +349,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, int supersampleFactor = testOptions.enableReferenceQuality ? 8 : 1; { - MX_TRACE_SCOPE(MX_TRACE_CAT_RENDER, "RenderMaterial"); + MX_TRACE_SCOPE(cat::Render, "RenderMaterial"); mx::ScopedTimer renderTimer(&profileTimes.languageTimes.renderTime); _renderer->getImageHandler()->setSearchPath(imageSearchPath); unsigned int width = (unsigned int) testOptions.renderSize[0] * supersampleFactor; From afb5b7793cb5bae8003b5a4f92a7ccf9540a300c Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Fri, 9 Jan 2026 17:18:15 -0500 Subject: [PATCH 09/40] Rename tracing classes to follow MaterialX conventions Rename files and classes to better align with MaterialX naming conventions and improve API clarity: Files: - MxTrace.h/cpp -> Tracing.h/cpp - MxTracePerfetto.h/cpp -> PerfettoSink.h/cpp Classes (now in MaterialX::Tracing namespace): - MxTraceBackend -> Tracing::Sink - MxTraceCollector -> Tracing::Dispatcher - MxTraceScope -> Tracing::Scope - MxTraceCategory -> Tracing::Category - MxPerfettoBackend -> Tracing::PerfettoSink Rationale: - 'Sink' is common terminology in logging/tracing for data destinations - 'Dispatcher' better describes the routing behavior (vs 'Collector') - Nested Tracing:: namespace groups related types cleanly - File names match primary class names (PerfettoSink.h) - Macros keep MX_ prefix for collision safety Usage example: namespace trace = mx::Tracing; auto sink = trace::PerfettoSink::create(); trace::Dispatcher::getInstance().setSink(sink); MX_TRACE_SCOPE(trace::Category::Render, "MyEvent"); --- source/MaterialXCore/CMakeLists.txt | 4 +- source/MaterialXCore/MxTrace.cpp | 22 ---- .../{MxTracePerfetto.cpp => PerfettoSink.cpp} | 43 +++---- .../{MxTracePerfetto.h => PerfettoSink.h} | 44 ++++---- source/MaterialXCore/Tracing.cpp | 27 +++++ source/MaterialXCore/{MxTrace.h => Tracing.h} | 106 ++++++++++-------- .../MaterialXRender/RenderUtil.cpp | 14 +-- .../MaterialXRenderGlsl/RenderGlsl.cpp | 4 +- 8 files changed, 145 insertions(+), 119 deletions(-) delete mode 100644 source/MaterialXCore/MxTrace.cpp rename source/MaterialXCore/{MxTracePerfetto.cpp => PerfettoSink.cpp} (79%) rename source/MaterialXCore/{MxTracePerfetto.h => PerfettoSink.h} (60%) create mode 100644 source/MaterialXCore/Tracing.cpp rename source/MaterialXCore/{MxTrace.h => Tracing.h} (63%) diff --git a/source/MaterialXCore/CMakeLists.txt b/source/MaterialXCore/CMakeLists.txt index dd26a84736..e8c1518667 100644 --- a/source/MaterialXCore/CMakeLists.txt +++ b/source/MaterialXCore/CMakeLists.txt @@ -5,8 +5,8 @@ file(GLOB materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h" "${CMAKE_CURRENT_B # Exclude all tracing implementation when tracing is disabled (zero overhead) if(NOT MATERIALX_BUILD_TRACING) - list(REMOVE_ITEM materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/MxTrace.cpp") - list(REMOVE_ITEM materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/MxTracePerfetto.cpp") + list(REMOVE_ITEM materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/Tracing.cpp") + list(REMOVE_ITEM materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/PerfettoSink.cpp") endif() mx_add_library(MaterialXCore diff --git a/source/MaterialXCore/MxTrace.cpp b/source/MaterialXCore/MxTrace.cpp deleted file mode 100644 index b2bf2a69db..0000000000 --- a/source/MaterialXCore/MxTrace.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright Contributors to the MaterialX Project -// SPDX-License-Identifier: Apache-2.0 -// - -#include - -MATERIALX_NAMESPACE_BEGIN - -MxTraceCollector& MxTraceCollector::getInstance() -{ - static MxTraceCollector instance; - return instance; -} - -void MxTraceCollector::setBackend(std::shared_ptr backend) -{ - _backend = backend; -} - -MATERIALX_NAMESPACE_END - diff --git a/source/MaterialXCore/MxTracePerfetto.cpp b/source/MaterialXCore/PerfettoSink.cpp similarity index 79% rename from source/MaterialXCore/MxTracePerfetto.cpp rename to source/MaterialXCore/PerfettoSink.cpp index c8cfa29283..ce577b8a2b 100644 --- a/source/MaterialXCore/MxTracePerfetto.cpp +++ b/source/MaterialXCore/PerfettoSink.cpp @@ -3,8 +3,8 @@ // SPDX-License-Identifier: Apache-2.0 // -#include -#include +#include +#include #ifdef MATERIALX_BUILD_TRACING @@ -43,25 +43,28 @@ PERFETTO_TRACK_EVENT_STATIC_STORAGE(); MATERIALX_NAMESPACE_BEGIN -class MxPerfettoBackend::Impl +namespace Tracing +{ + +class PerfettoSink::Impl { public: std::unique_ptr session; }; -MxPerfettoBackend::MxPerfettoBackend() : _impl(new Impl()) +PerfettoSink::PerfettoSink() : _impl(new Impl()) { } -MxPerfettoBackend::~MxPerfettoBackend() = default; +PerfettoSink::~PerfettoSink() = default; -std::shared_ptr MxPerfettoBackend::create() +std::shared_ptr PerfettoSink::create() { // Use shared_ptr with custom destructor to handle private constructor - return std::shared_ptr(new MxPerfettoBackend()); + return std::shared_ptr(new PerfettoSink()); } -void MxPerfettoBackend::initialize(size_t bufferSizeKb) +void PerfettoSink::initialize(size_t bufferSizeKb) { // Initialize Perfetto with in-process backend perfetto::TracingInitArgs args; @@ -84,7 +87,7 @@ void MxPerfettoBackend::initialize(size_t bufferSizeKb) _impl->session->StartBlocking(); } -void MxPerfettoBackend::shutdown(const std::string& outputPath) +void PerfettoSink::shutdown(const std::string& outputPath) { if (!_impl->session) return; @@ -112,24 +115,24 @@ static bool categoryMatches(const char* category, const char* target) return category == target || (category && std::strcmp(category, target) == 0); } -void MxPerfettoBackend::beginEvent(const char* category, const char* name) +void PerfettoSink::beginEvent(const char* category, const char* name) { // Perfetto requires compile-time category names for TRACE_EVENT macros. // We dispatch based on the runtime category to the appropriate compile-time macro. // Pointer comparison catches constexpr usage, strcmp handles dynamic strings. - if (categoryMatches(category, MxTraceCategory::ShaderGen)) + if (categoryMatches(category, Category::ShaderGen)) { TRACE_EVENT_BEGIN("mx.shadergen", nullptr, [&](perfetto::EventContext ctx) { ctx.event()->set_name(name); }); } - else if (categoryMatches(category, MxTraceCategory::Optimize)) + else if (categoryMatches(category, Category::Optimize)) { TRACE_EVENT_BEGIN("mx.optimize", nullptr, [&](perfetto::EventContext ctx) { ctx.event()->set_name(name); }); } - else if (categoryMatches(category, MxTraceCategory::Material)) + else if (categoryMatches(category, Category::Material)) { TRACE_EVENT_BEGIN("mx.material", nullptr, [&](perfetto::EventContext ctx) { ctx.event()->set_name(name); @@ -143,18 +146,18 @@ void MxPerfettoBackend::beginEvent(const char* category, const char* name) } } -void MxPerfettoBackend::endEvent(const char* category) +void PerfettoSink::endEvent(const char* category) { // Must match the category used in beginEvent - if (categoryMatches(category, MxTraceCategory::ShaderGen)) + if (categoryMatches(category, Category::ShaderGen)) { TRACE_EVENT_END("mx.shadergen"); } - else if (categoryMatches(category, MxTraceCategory::Optimize)) + else if (categoryMatches(category, Category::Optimize)) { TRACE_EVENT_END("mx.optimize"); } - else if (categoryMatches(category, MxTraceCategory::Material)) + else if (categoryMatches(category, Category::Material)) { TRACE_EVENT_END("mx.material"); } @@ -164,7 +167,7 @@ void MxPerfettoBackend::endEvent(const char* category) } } -void MxPerfettoBackend::counter(const char* category, const char* name, double value) +void PerfettoSink::counter(const char* category, const char* name, double value) { (void)category; // Create a counter track with the given name @@ -172,7 +175,7 @@ void MxPerfettoBackend::counter(const char* category, const char* name, double v TRACE_COUNTER("mx.render", track, value); } -void MxPerfettoBackend::setThreadName(const char* name) +void PerfettoSink::setThreadName(const char* name) { // Set thread name for trace visualization auto track = perfetto::ThreadTrack::Current(); @@ -181,6 +184,8 @@ void MxPerfettoBackend::setThreadName(const char* name) perfetto::TrackEvent::SetTrackDescriptor(track, desc); } +} // namespace Tracing + MATERIALX_NAMESPACE_END #endif // MATERIALX_BUILD_TRACING diff --git a/source/MaterialXCore/MxTracePerfetto.h b/source/MaterialXCore/PerfettoSink.h similarity index 60% rename from source/MaterialXCore/MxTracePerfetto.h rename to source/MaterialXCore/PerfettoSink.h index ac6f674c09..8c31ea8cf7 100644 --- a/source/MaterialXCore/MxTracePerfetto.h +++ b/source/MaterialXCore/PerfettoSink.h @@ -3,23 +3,24 @@ // SPDX-License-Identifier: Apache-2.0 // -#ifndef MATERIALX_MXTRACEPERFETTO_H -#define MATERIALX_MXTRACEPERFETTO_H +#ifndef MATERIALX_PERFETTOSINK_H +#define MATERIALX_PERFETTOSINK_H /// @file -/// Perfetto-based implementation of the MxTraceBackend interface. +/// Perfetto-based implementation of the Tracing::Sink interface. /// /// Usage: -/// #include -/// auto backend = mx::MxPerfettoBackend::create(); -/// backend->initialize(); -/// mx::MxTraceCollector::getInstance().setBackend(backend); +/// #include +/// namespace trace = mx::Tracing; +/// auto sink = trace::PerfettoSink::create(); +/// sink->initialize(); +/// trace::Dispatcher::getInstance().setSink(sink); /// // ... run application with tracing ... -/// mx::MxTraceCollector::getInstance().setBackend(nullptr); -/// backend->shutdown("trace.perfetto-trace"); +/// trace::Dispatcher::getInstance().setSink(nullptr); +/// sink->shutdown("trace.perfetto-trace"); /// // Open the .perfetto-trace file at https://ui.perfetto.dev -#include +#include #ifdef MATERIALX_BUILD_TRACING @@ -28,19 +29,22 @@ MATERIALX_NAMESPACE_BEGIN -/// @class MxPerfettoBackend -/// Perfetto-based implementation of MxTraceBackend. +namespace Tracing +{ + +/// @class PerfettoSink +/// Perfetto-based implementation of Tracing::Sink. /// /// This class provides a concrete implementation using Perfetto SDK's /// in-process tracing backend. Trace data is written to a .perfetto-trace /// file that can be visualized at https://ui.perfetto.dev -class MX_CORE_API MxPerfettoBackend : public MxTraceBackend +class MX_CORE_API PerfettoSink : public Sink { public: - /// Create a new Perfetto backend instance. - static std::shared_ptr create(); + /// Create a new Perfetto sink instance. + static std::shared_ptr create(); - ~MxPerfettoBackend() override; + ~PerfettoSink() override; /// Initialize Perfetto tracing. Must be called before any trace events. /// @param bufferSizeKb Size of the trace buffer in KB (default 32MB) @@ -50,22 +54,24 @@ class MX_CORE_API MxPerfettoBackend : public MxTraceBackend /// @param outputPath Path to write the trace file (e.g., "trace.perfetto-trace") void shutdown(const std::string& outputPath); - // MxTraceBackend interface implementation + // Sink interface implementation void beginEvent(const char* category, const char* name) override; void endEvent(const char* category) override; void counter(const char* category, const char* name, double value) override; void setThreadName(const char* name) override; private: - MxPerfettoBackend(); + PerfettoSink(); class Impl; std::unique_ptr _impl; }; +} // namespace Tracing + MATERIALX_NAMESPACE_END #endif // MATERIALX_BUILD_TRACING -#endif // MATERIALX_MXTRACEPERFETTO_H +#endif // MATERIALX_PERFETTOSINK_H diff --git a/source/MaterialXCore/Tracing.cpp b/source/MaterialXCore/Tracing.cpp new file mode 100644 index 0000000000..f9ef16ce0d --- /dev/null +++ b/source/MaterialXCore/Tracing.cpp @@ -0,0 +1,27 @@ +// +// Copyright Contributors to the MaterialX Project +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +MATERIALX_NAMESPACE_BEGIN + +namespace Tracing +{ + +Dispatcher& Dispatcher::getInstance() +{ + static Dispatcher instance; + return instance; +} + +void Dispatcher::setSink(std::shared_ptr sink) +{ + _sink = sink; +} + +} // namespace Tracing + +MATERIALX_NAMESPACE_END + diff --git a/source/MaterialXCore/MxTrace.h b/source/MaterialXCore/Tracing.h similarity index 63% rename from source/MaterialXCore/MxTrace.h rename to source/MaterialXCore/Tracing.h index 3bb5223b6e..bc0452de40 100644 --- a/source/MaterialXCore/MxTrace.h +++ b/source/MaterialXCore/Tracing.h @@ -3,8 +3,8 @@ // SPDX-License-Identifier: Apache-2.0 // -#ifndef MATERIALX_MXTRACE_H -#define MATERIALX_MXTRACE_H +#ifndef MATERIALX_TRACING_H +#define MATERIALX_TRACING_H /// @file /// Tracing infrastructure for performance analysis. @@ -14,7 +14,7 @@ /// /// Design goals: /// - API similar to USD's TraceCollector/TraceScope for familiarity -/// - Abstract backend allows USD to inject its own tracing when calling MaterialX +/// - Abstract sink allows USD to inject its own tracing when calling MaterialX /// - Zero overhead when tracing is disabled (macros compile to nothing) /// - Constexpr category strings for compile-time validation @@ -25,13 +25,18 @@ MATERIALX_NAMESPACE_BEGIN -/// @namespace MxTraceCategory +/// @namespace Tracing +/// Tracing infrastructure for performance analysis. +namespace Tracing +{ + +/// @namespace Category /// Constexpr trace category identifiers. /// /// These are compile-time constant strings that identify trace event categories. /// Using constexpr ensures type safety and enables compile-time validation. /// The USD TraceCollector backend can map these to its own categories. -namespace MxTraceCategory +namespace Category { /// Rendering operations (GPU commands, frame capture, etc.) constexpr const char* Render = "mx.render"; @@ -47,18 +52,18 @@ namespace MxTraceCategory } // Usage: Add a namespace alias in your .cpp file for brevity: -// namespace cat = MaterialX::MxTraceCategory; -// MX_TRACE_SCOPE(cat::Render, "MyEvent"); +// namespace trace = MaterialX::Tracing; +// MX_TRACE_SCOPE(trace::Category::Render, "MyEvent"); -/// @class MxTraceBackend -/// Abstract tracing backend interface. +/// @class Sink +/// Abstract tracing sink interface. /// /// Implementations can delegate to Perfetto, USD TraceCollector, or custom systems. /// This allows USD/Hydra to inject their own tracing when calling MaterialX code. -class MX_CORE_API MxTraceBackend +class MX_CORE_API Sink { public: - virtual ~MxTraceBackend() = default; + virtual ~Sink() = default; /// Begin a trace event with the given category and name. virtual void beginEvent(const char* category, const char* name) = 0; @@ -73,90 +78,95 @@ class MX_CORE_API MxTraceBackend virtual void setThreadName(const char* name) = 0; }; -/// @class MxTraceCollector -/// Global trace collector singleton (similar to USD's TraceCollector). +/// @class Dispatcher +/// Global trace dispatcher singleton. /// +/// Holds a reference to the active sink and dispatches trace events to it. +/// Similar in role to USD's TraceCollector but acts as a passthrough dispatcher. +/// /// Usage: -/// MxTraceCollector::getInstance().setBackend(myBackend); -/// MxTraceCollector::getInstance().beginEvent("mx.render", "RenderFrame"); -class MX_CORE_API MxTraceCollector +/// Tracing::Dispatcher::getInstance().setSink(mySink); +/// Tracing::Dispatcher::getInstance().beginEvent("mx.render", "RenderFrame"); +class MX_CORE_API Dispatcher { public: /// Get the singleton instance. - static MxTraceCollector& getInstance(); + static Dispatcher& getInstance(); - /// Set the tracing backend. Pass nullptr to disable tracing. - void setBackend(std::shared_ptr backend); + /// Set the tracing sink. Pass nullptr to disable tracing. + void setSink(std::shared_ptr sink); - /// Get the current backend (may be nullptr). - MxTraceBackend* getBackend() const { return _backend.get(); } + /// Get the current sink (may be nullptr). + Sink* getSink() const { return _sink.get(); } /// Check if tracing is currently enabled. - bool isEnabled() const { return _backend != nullptr; } + bool isEnabled() const { return _sink != nullptr; } /// Begin a trace event. void beginEvent(const char* category, const char* name) { - if (_backend) - _backend->beginEvent(category, name); + if (_sink) + _sink->beginEvent(category, name); } /// End a trace event. void endEvent(const char* category) { - if (_backend) - _backend->endEvent(category); + if (_sink) + _sink->endEvent(category); } /// Record a counter value. void counter(const char* category, const char* name, double value) { - if (_backend) - _backend->counter(category, name, value); + if (_sink) + _sink->counter(category, name, value); } private: - MxTraceCollector() = default; - MxTraceCollector(const MxTraceCollector&) = delete; - MxTraceCollector& operator=(const MxTraceCollector&) = delete; + Dispatcher() = default; + Dispatcher(const Dispatcher&) = delete; + Dispatcher& operator=(const Dispatcher&) = delete; - std::shared_ptr _backend; + std::shared_ptr _sink; }; -/// @class MxTraceScope +/// @class Scope /// RAII scope guard for trace events (similar to USD's TraceScope). /// /// Usage: /// { -/// MxTraceScope scope("mx.render", "RenderMaterial"); +/// Tracing::Scope scope("mx.render", "RenderMaterial"); /// // ... code to trace ... /// } // Event automatically ends here -class MX_CORE_API MxTraceScope +class MX_CORE_API Scope { public: - MxTraceScope(const char* category, const char* name) + Scope(const char* category, const char* name) : _category(category) - , _enabled(MxTraceCollector::getInstance().isEnabled()) + , _enabled(Dispatcher::getInstance().isEnabled()) { if (_enabled) - MxTraceCollector::getInstance().beginEvent(category, name); + Dispatcher::getInstance().beginEvent(category, name); } - ~MxTraceScope() + ~Scope() { if (_enabled) - MxTraceCollector::getInstance().endEvent(_category); + Dispatcher::getInstance().endEvent(_category); } // Non-copyable - MxTraceScope(const MxTraceScope&) = delete; - MxTraceScope& operator=(const MxTraceScope&) = delete; + Scope(const Scope&) = delete; + Scope& operator=(const Scope&) = delete; private: const char* _category; bool _enabled; }; +} // namespace Tracing + MATERIALX_NAMESPACE_END // ============================================================================ @@ -173,23 +183,23 @@ MATERIALX_NAMESPACE_END /// Create a scoped trace event. Event ends when scope exits. #define MX_TRACE_SCOPE(category, name) \ - MaterialX::MxTraceScope MX_TRACE_CONCAT(_mxTraceScope_, __LINE__)(category, name) + MaterialX::Tracing::Scope MX_TRACE_CONCAT(_mxTraceScope_, __LINE__)(category, name) /// Create a scoped trace event using the current function name. #define MX_TRACE_FUNCTION(category) \ - MaterialX::MxTraceScope MX_TRACE_CONCAT(_mxTraceFn_, __LINE__)(category, __FUNCTION__) + MaterialX::Tracing::Scope MX_TRACE_CONCAT(_mxTraceFn_, __LINE__)(category, __FUNCTION__) /// Record a counter value. #define MX_TRACE_COUNTER(category, name, value) \ - MaterialX::MxTraceCollector::getInstance().counter(category, name, value) + MaterialX::Tracing::Dispatcher::getInstance().counter(category, name, value) /// Begin a trace event (must be paired with MX_TRACE_END). #define MX_TRACE_BEGIN(category, name) \ - MaterialX::MxTraceCollector::getInstance().beginEvent(category, name) + MaterialX::Tracing::Dispatcher::getInstance().beginEvent(category, name) /// End a trace event. #define MX_TRACE_END(category) \ - MaterialX::MxTraceCollector::getInstance().endEvent(category) + MaterialX::Tracing::Dispatcher::getInstance().endEvent(category) #else // MATERIALX_BUILD_TRACING not defined @@ -201,5 +211,5 @@ MATERIALX_NAMESPACE_END #endif // MATERIALX_BUILD_TRACING -#endif // MATERIALX_MXTRACE_H +#endif // MATERIALX_TRACING_H diff --git a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp index 0bd5a5eafb..16c46751ee 100644 --- a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp +++ b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp @@ -13,8 +13,8 @@ #endif #ifdef MATERIALX_BUILD_TRACING -#include -#include +#include +#include #endif namespace mx = MaterialX; @@ -102,9 +102,9 @@ bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) #ifdef MATERIALX_BUILD_TRACING // Initialize tracing with target-specific trace filename mx::FilePath tracePath = options.resolveOutputPath(_shaderGenerator->getTarget() + "_render_trace.perfetto-trace"); - auto perfettoBackend = mx::MxPerfettoBackend::create(); - perfettoBackend->initialize(); - mx::MxTraceCollector::getInstance().setBackend(perfettoBackend); + auto perfettoSink = mx::Tracing::PerfettoSink::create(); + perfettoSink->initialize(); + mx::Tracing::Dispatcher::getInstance().setSink(perfettoSink); #endif #ifdef LOG_TO_FILE @@ -339,8 +339,8 @@ bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) #ifdef MATERIALX_BUILD_TRACING // Shutdown tracing and write trace file - mx::MxTraceCollector::getInstance().setBackend(nullptr); - perfettoBackend->shutdown(tracePath.asString()); + mx::Tracing::Dispatcher::getInstance().setSink(nullptr); + perfettoSink->shutdown(tracePath.asString()); #endif // Print effective output directory for easy access (clickable in terminals) diff --git a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp index 50ba182a4f..ba34f94118 100644 --- a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp +++ b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #if defined(MATERIALX_BUILD_OIIO) #include #endif @@ -20,7 +20,7 @@ #include namespace mx = MaterialX; -namespace cat = mx::MxTraceCategory; +namespace cat = mx::Tracing::Category; // // Render validation tester for the GLSL shading language From 1cb1b4eb5bef842ef1e398f232d73a06c1df736f Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Mon, 12 Jan 2026 09:52:55 -0500 Subject: [PATCH 10/40] Add clarifying comments for Perfetto SDK integration - Explain amalgamated source compilation is Google's official recommended approach per https://perfetto.dev/docs/instrumentation/tracing-sdk - Document ws2_32 dependency (Windows Sockets 2 for Perfetto IPC) --- source/MaterialXCore/CMakeLists.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/source/MaterialXCore/CMakeLists.txt b/source/MaterialXCore/CMakeLists.txt index e8c1518667..980e37d6d6 100644 --- a/source/MaterialXCore/CMakeLists.txt +++ b/source/MaterialXCore/CMakeLists.txt @@ -24,7 +24,11 @@ target_include_directories(${TARGET_NAME} # Perfetto tracing support if(MATERIALX_BUILD_TRACING) - # Add Perfetto SDK source file (single-file amalgamation) + # The Perfetto SDK is distributed as an amalgamated single-file source (sdk/perfetto.cc). + # This is the official recommended integration method per Google's documentation: + # https://perfetto.dev/docs/instrumentation/tracing-sdk + # Compiling directly (vs. linking a library) simplifies integration and enables + # better compiler optimizations within a single translation unit. target_sources(${TARGET_NAME} PRIVATE "${perfetto_SOURCE_DIR}/sdk/perfetto.cc") # Use generator expression - only needed at build time, not install @@ -32,7 +36,8 @@ if(MATERIALX_BUILD_TRACING) $) target_compile_definitions(${TARGET_NAME} PUBLIC MATERIALX_BUILD_TRACING) if(WIN32) - # Perfetto requires ws2_32 on Windows + # ws2_32: Windows Sockets 2 library. Perfetto uses Winsock internally for + # inter-process communication (IPC), even in single-process tracing mode. target_link_libraries(${TARGET_NAME} PRIVATE ws2_32) # Prevent Windows.h min/max macros from breaking std::numeric_limits # Also need /bigobj because perfetto.cc has too many sections for MSVC From afa4c2af19926fa15d61d0292f75584a067c0181 Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Mon, 12 Jan 2026 13:46:25 -0500 Subject: [PATCH 11/40] Refactor tracing API for cleaner design Dispatcher: - Takes ownership of sink via unique_ptr (setSink) - Explicit shutdownSink() destroys sink and writes output - Asserts on double-set to catch programming errors PerfettoSink: - Constructor takes output path, starts session immediately - Destructor writes trace file (true RAII) - Uses std::call_once for thread-safe global Perfetto init - Removed pimpl - simpler since only setup code includes this header Scope: - Removed _enabled caching - Dispatcher checks internally - One less bool on the stack per trace scope Usage: - Added TracingGuard scope guard in RenderUtil.cpp for exception safety - Added const to immutable members (_outputPath, _category) The design now follows proper RAII patterns with clear ownership. --- source/MaterialXCore/PerfettoSink.cpp | 76 ++++++------------- source/MaterialXCore/PerfettoSink.h | 69 +++++++++++------ source/MaterialXCore/Tracing.cpp | 14 +++- source/MaterialXCore/Tracing.h | 33 ++++---- .../MaterialXRender/RenderUtil.cpp | 15 ++-- 5 files changed, 101 insertions(+), 106 deletions(-) diff --git a/source/MaterialXCore/PerfettoSink.cpp b/source/MaterialXCore/PerfettoSink.cpp index ce577b8a2b..9b36a98a95 100644 --- a/source/MaterialXCore/PerfettoSink.cpp +++ b/source/MaterialXCore/PerfettoSink.cpp @@ -4,26 +4,12 @@ // #include -#include #ifdef MATERIALX_BUILD_TRACING -// Suppress verbose warnings from Perfetto SDK templates -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4127) // conditional expression is constant -#pragma warning(disable : 4146) // unary minus on unsigned type -#pragma warning(disable : 4369) // enumerator value cannot be represented -#endif - -#include - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - #include #include +#include // Define Perfetto trace categories for MaterialX // These must be in a .cpp file, not a header @@ -46,67 +32,50 @@ MATERIALX_NAMESPACE_BEGIN namespace Tracing { -class PerfettoSink::Impl -{ - public: - std::unique_ptr session; -}; - -PerfettoSink::PerfettoSink() : _impl(new Impl()) -{ -} - -PerfettoSink::~PerfettoSink() = default; +// One-time global Perfetto initialization flag +static std::once_flag g_perfettoInitFlag; -std::shared_ptr PerfettoSink::create() +PerfettoSink::PerfettoSink(std::string outputPath, size_t bufferSizeKb) + : _outputPath(std::move(outputPath)) { - // Use shared_ptr with custom destructor to handle private constructor - return std::shared_ptr(new PerfettoSink()); -} - -void PerfettoSink::initialize(size_t bufferSizeKb) -{ - // Initialize Perfetto with in-process backend - perfetto::TracingInitArgs args; - args.backends |= perfetto::kInProcessBackend; - perfetto::Tracing::Initialize(args); - - // Register track event data source - perfetto::TrackEvent::Register(); - - // Configure tracing session + // One-time global Perfetto initialization (safe to call from multiple instances) + std::call_once(g_perfettoInitFlag, []() { + perfetto::TracingInitArgs args; + args.backends |= perfetto::kInProcessBackend; + perfetto::Tracing::Initialize(args); + perfetto::TrackEvent::Register(); + }); + + // Create and start a tracing session for this sink perfetto::TraceConfig cfg; cfg.add_buffers()->set_size_kb(static_cast(bufferSizeKb)); auto* ds_cfg = cfg.add_data_sources()->mutable_config(); ds_cfg->set_name("track_event"); - // Start tracing session - _impl->session = perfetto::Tracing::NewTrace(); - _impl->session->Setup(cfg); - _impl->session->StartBlocking(); + _session = perfetto::Tracing::NewTrace(); + _session->Setup(cfg); + _session->StartBlocking(); } -void PerfettoSink::shutdown(const std::string& outputPath) +PerfettoSink::~PerfettoSink() { - if (!_impl->session) + if (!_session) return; // Flush any pending trace data perfetto::TrackEvent::Flush(); // Stop the tracing session - _impl->session->StopBlocking(); + _session->StopBlocking(); // Read trace data and write to file - std::vector traceData(_impl->session->ReadTraceBlocking()); + std::vector traceData(_session->ReadTraceBlocking()); if (!traceData.empty()) { - std::ofstream output(outputPath, std::ios::binary); + std::ofstream output(_outputPath, std::ios::binary); output.write(traceData.data(), static_cast(traceData.size())); } - - _impl->session.reset(); } // Helper to check category match (pointer comparison first for constexpr, then strcmp) @@ -189,4 +158,3 @@ void PerfettoSink::setThreadName(const char* name) MATERIALX_NAMESPACE_END #endif // MATERIALX_BUILD_TRACING - diff --git a/source/MaterialXCore/PerfettoSink.h b/source/MaterialXCore/PerfettoSink.h index 8c31ea8cf7..0ea0841eaf 100644 --- a/source/MaterialXCore/PerfettoSink.h +++ b/source/MaterialXCore/PerfettoSink.h @@ -12,18 +12,39 @@ /// Usage: /// #include /// namespace trace = mx::Tracing; -/// auto sink = trace::PerfettoSink::create(); -/// sink->initialize(); -/// trace::Dispatcher::getInstance().setSink(sink); -/// // ... run application with tracing ... -/// trace::Dispatcher::getInstance().setSink(nullptr); -/// sink->shutdown("trace.perfetto-trace"); +/// +/// trace::Dispatcher::getInstance().setSink( +/// std::make_unique("trace.perfetto-trace")); +/// +/// // Use a local scope guard for exception safety +/// struct SinkGuard { +/// ~SinkGuard() { trace::Dispatcher::getInstance().shutdownSink(); } +/// } guard; +/// +/// // ... traced work ... +/// +/// // guard destructor calls shutdownSink(), which destroys PerfettoSink, +/// // which writes the trace file to the path specified in constructor. /// // Open the .perfetto-trace file at https://ui.perfetto.dev #include #ifdef MATERIALX_BUILD_TRACING +// Suppress verbose warnings from Perfetto SDK templates +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4127) // conditional expression is constant +#pragma warning(disable : 4146) // unary minus on unsigned type +#pragma warning(disable : 4369) // enumerator value cannot be represented +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + #include #include @@ -36,23 +57,28 @@ namespace Tracing /// Perfetto-based implementation of Tracing::Sink. /// /// This class provides a concrete implementation using Perfetto SDK's -/// in-process tracing backend. Trace data is written to a .perfetto-trace -/// file that can be visualized at https://ui.perfetto.dev +/// in-process tracing backend. The constructor starts a tracing session, +/// and the destructor stops the session and writes trace data to a +/// .perfetto-trace file that can be visualized at https://ui.perfetto.dev +/// +/// Multiple PerfettoSink instances can coexist (each with its own session), +/// though typically only one is active at a time via the Dispatcher. class MX_CORE_API PerfettoSink : public Sink { public: - /// Create a new Perfetto sink instance. - static std::shared_ptr create(); - - ~PerfettoSink() override; - - /// Initialize Perfetto tracing. Must be called before any trace events. + /// Construct and start a Perfetto tracing session. + /// @param outputPath Path to write the trace file when destroyed /// @param bufferSizeKb Size of the trace buffer in KB (default 32MB) - void initialize(size_t bufferSizeKb = 32768); + explicit PerfettoSink(std::string outputPath, size_t bufferSizeKb = 32768); + + /// Stop tracing and write the trace to the output path. + ~PerfettoSink() override; - /// Stop tracing and write the trace to a file. - /// @param outputPath Path to write the trace file (e.g., "trace.perfetto-trace") - void shutdown(const std::string& outputPath); + // Non-copyable, non-movable + PerfettoSink(const PerfettoSink&) = delete; + PerfettoSink& operator=(const PerfettoSink&) = delete; + PerfettoSink(PerfettoSink&&) = delete; + PerfettoSink& operator=(PerfettoSink&&) = delete; // Sink interface implementation void beginEvent(const char* category, const char* name) override; @@ -61,10 +87,8 @@ class MX_CORE_API PerfettoSink : public Sink void setThreadName(const char* name) override; private: - PerfettoSink(); - - class Impl; - std::unique_ptr _impl; + const std::string _outputPath; + std::unique_ptr _session; }; } // namespace Tracing @@ -74,4 +98,3 @@ MATERIALX_NAMESPACE_END #endif // MATERIALX_BUILD_TRACING #endif // MATERIALX_PERFETTOSINK_H - diff --git a/source/MaterialXCore/Tracing.cpp b/source/MaterialXCore/Tracing.cpp index f9ef16ce0d..a3a9ebe1fe 100644 --- a/source/MaterialXCore/Tracing.cpp +++ b/source/MaterialXCore/Tracing.cpp @@ -16,12 +16,20 @@ Dispatcher& Dispatcher::getInstance() return instance; } -void Dispatcher::setSink(std::shared_ptr sink) +void Dispatcher::setSink(std::unique_ptr sink) { - _sink = sink; + // Assert if a sink is already set - caller should shutdownSink() first. + // This catches programming errors; if triggered, the old sink will still + // write its output when destroyed by this assignment. + assert(!_sink && "Sink already set - call shutdownSink() first"); + _sink = std::move(sink); +} + +void Dispatcher::shutdownSink() +{ + _sink.reset(); // Destructor handles writing output } } // namespace Tracing MATERIALX_NAMESPACE_END - diff --git a/source/MaterialXCore/Tracing.h b/source/MaterialXCore/Tracing.h index bc0452de40..3c9d01ee7f 100644 --- a/source/MaterialXCore/Tracing.h +++ b/source/MaterialXCore/Tracing.h @@ -20,6 +20,7 @@ #include +#include #include #include @@ -81,23 +82,26 @@ class MX_CORE_API Sink /// @class Dispatcher /// Global trace dispatcher singleton. /// -/// Holds a reference to the active sink and dispatches trace events to it. -/// Similar in role to USD's TraceCollector but acts as a passthrough dispatcher. +/// Owns the active sink and dispatches trace events to it. +/// The Dispatcher takes ownership of the sink via unique_ptr. /// /// Usage: -/// Tracing::Dispatcher::getInstance().setSink(mySink); -/// Tracing::Dispatcher::getInstance().beginEvent("mx.render", "RenderFrame"); +/// Dispatcher::getInstance().setSink(std::make_unique(...)); +/// // ... traced work ... +/// Dispatcher::getInstance().shutdownSink(); class MX_CORE_API Dispatcher { public: /// Get the singleton instance. static Dispatcher& getInstance(); - /// Set the tracing sink. Pass nullptr to disable tracing. - void setSink(std::shared_ptr sink); + /// Set the tracing sink. Takes ownership. + /// Asserts if a sink is already set (call shutdownSink() first). + void setSink(std::unique_ptr sink); - /// Get the current sink (may be nullptr). - Sink* getSink() const { return _sink.get(); } + /// Shutdown and destroy the current sink. + /// The sink's destructor handles writing output. + void shutdownSink(); /// Check if tracing is currently enabled. bool isEnabled() const { return _sink != nullptr; } @@ -128,7 +132,7 @@ class MX_CORE_API Dispatcher Dispatcher(const Dispatcher&) = delete; Dispatcher& operator=(const Dispatcher&) = delete; - std::shared_ptr _sink; + std::unique_ptr _sink; }; /// @class Scope @@ -144,16 +148,13 @@ class MX_CORE_API Scope public: Scope(const char* category, const char* name) : _category(category) - , _enabled(Dispatcher::getInstance().isEnabled()) { - if (_enabled) - Dispatcher::getInstance().beginEvent(category, name); + Dispatcher::getInstance().beginEvent(category, name); } ~Scope() { - if (_enabled) - Dispatcher::getInstance().endEvent(_category); + Dispatcher::getInstance().endEvent(_category); } // Non-copyable @@ -161,8 +162,7 @@ class MX_CORE_API Scope Scope& operator=(const Scope&) = delete; private: - const char* _category; - bool _enabled; + const char* const _category; }; } // namespace Tracing @@ -212,4 +212,3 @@ MATERIALX_NAMESPACE_END #endif // MATERIALX_BUILD_TRACING #endif // MATERIALX_TRACING_H - diff --git a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp index 16c46751ee..14873bdf2b 100644 --- a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp +++ b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp @@ -102,9 +102,12 @@ bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) #ifdef MATERIALX_BUILD_TRACING // Initialize tracing with target-specific trace filename mx::FilePath tracePath = options.resolveOutputPath(_shaderGenerator->getTarget() + "_render_trace.perfetto-trace"); - auto perfettoSink = mx::Tracing::PerfettoSink::create(); - perfettoSink->initialize(); - mx::Tracing::Dispatcher::getInstance().setSink(perfettoSink); + mx::Tracing::Dispatcher::getInstance().setSink( + std::make_unique(tracePath.asString())); + // Scope guard ensures tracing is shut down on any exit path (return, exception, etc.) + struct TracingGuard { + ~TracingGuard() { mx::Tracing::Dispatcher::getInstance().shutdownSink(); } + } tracingGuard; #endif #ifdef LOG_TO_FILE @@ -337,12 +340,6 @@ bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) totalTime.endTimer(); printRunLog(profileTimes, options, profilingLog, dependLib); -#ifdef MATERIALX_BUILD_TRACING - // Shutdown tracing and write trace file - mx::Tracing::Dispatcher::getInstance().setSink(nullptr); - perfettoSink->shutdown(tracePath.asString()); -#endif - // Print effective output directory for easy access (clickable in terminals) if (!options.outputDirectory.isEmpty()) { From 6e1c923e0c9a39e7cde07d28bb81d608d0025599 Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Mon, 12 Jan 2026 13:57:17 -0500 Subject: [PATCH 12/40] Add Dispatcher::ShutdownGuard nested struct Move the scope guard pattern into the Dispatcher class for reusability. Callers can now use mx::Tracing::Dispatcher::ShutdownGuard instead of defining their own local struct. --- source/MaterialXCore/Tracing.h | 16 ++++++++++++++++ .../MaterialXTest/MaterialXRender/RenderUtil.cpp | 4 +--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/source/MaterialXCore/Tracing.h b/source/MaterialXCore/Tracing.h index 3c9d01ee7f..b93d3cfafe 100644 --- a/source/MaterialXCore/Tracing.h +++ b/source/MaterialXCore/Tracing.h @@ -103,6 +103,22 @@ class MX_CORE_API Dispatcher /// The sink's destructor handles writing output. void shutdownSink(); + /// Scope guard that calls shutdownSink() on destruction. + /// Ensures tracing is properly shut down on any exit path (return, exception, etc.) + /// + /// Usage: + /// Dispatcher::getInstance().setSink(std::make_unique(...)); + /// Dispatcher::ShutdownGuard guard; + /// // ... traced work ... + /// // guard destructor calls shutdownSink() + struct ShutdownGuard + { + ~ShutdownGuard() { Dispatcher::getInstance().shutdownSink(); } + ShutdownGuard() = default; + ShutdownGuard(const ShutdownGuard&) = delete; + ShutdownGuard& operator=(const ShutdownGuard&) = delete; + }; + /// Check if tracing is currently enabled. bool isEnabled() const { return _sink != nullptr; } diff --git a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp index 14873bdf2b..2bb8595e0e 100644 --- a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp +++ b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp @@ -105,9 +105,7 @@ bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) mx::Tracing::Dispatcher::getInstance().setSink( std::make_unique(tracePath.asString())); // Scope guard ensures tracing is shut down on any exit path (return, exception, etc.) - struct TracingGuard { - ~TracingGuard() { mx::Tracing::Dispatcher::getInstance().shutdownSink(); } - } tracingGuard; + mx::Tracing::Dispatcher::ShutdownGuard tracingGuard; #endif #ifdef LOG_TO_FILE From 5f84868732dfc71a3f407f4c31438748e4c506f4 Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Mon, 12 Jan 2026 14:05:40 -0500 Subject: [PATCH 13/40] Add enableTracing runtime option in _options.mtlx - New 'enableTracing' boolean option (default: false) - Parsed in TestSuiteOptions::readOptions() - RenderUtil.cpp conditionally initializes PerfettoSink based on this option - Uses std::optional for conditional scope guard - Allows profiling to be enabled/disabled without rebuilding --- resources/Materials/TestSuite/_options.mtlx | 6 ++++++ .../MaterialXGenShader/GenShaderUtil.cpp | 6 ++++++ .../MaterialXGenShader/GenShaderUtil.h | 4 ++++ .../MaterialXRender/RenderUtil.cpp | 17 +++++++++++------ 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/resources/Materials/TestSuite/_options.mtlx b/resources/Materials/TestSuite/_options.mtlx index 9f75172a5a..95a7b80f25 100644 --- a/resources/Materials/TestSuite/_options.mtlx +++ b/resources/Materials/TestSuite/_options.mtlx @@ -78,5 +78,11 @@ The directory will be created if it doesn't exist. --> + + + diff --git a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp index 8055bec34a..0ef9d544b3 100644 --- a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp +++ b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp @@ -986,6 +986,7 @@ void TestSuiteOptions::print(std::ostream& output) const output << "\tRender test paths: " << renderTestPaths.asString() << std::endl; output << "\tEnable Reference Quality: " << enableReferenceQuality << std::endl; output << "\tOutput Directory: " << (outputDirectory.isEmpty() ? "(default)" : outputDirectory.asString()) << std::endl; + output << "\tEnable Tracing: " << enableTracing << std::endl; } bool TestSuiteOptions::readOptions(const std::string& optionFile) @@ -1012,6 +1013,7 @@ bool TestSuiteOptions::readOptions(const std::string& optionFile) const std::string RENDER_TEST_PATHS("renderTestPaths"); const std::string ENABLE_REFERENCE_QUALITY("enableReferenceQuality"); const std::string OUTPUT_DIRECTORY_STRING("outputDirectory"); + const std::string ENABLE_TRACING_STRING("enableTracing"); overrideFiles.clear(); dumpGeneratedCode = false; @@ -1123,6 +1125,10 @@ bool TestSuiteOptions::readOptions(const std::string& optionFile) } } } + else if (name == ENABLE_TRACING_STRING) + { + enableTracing = val->asA(); + } } } } diff --git a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h index 09485ec966..b5683787c0 100644 --- a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h +++ b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h @@ -122,6 +122,10 @@ class TestSuiteOptions // If empty, use default locations. If set, all artifacts go to this directory. mx::FilePath outputDirectory; + // Enable Perfetto tracing during render tests (requires MATERIALX_BUILD_TRACING). + // Default is false to avoid overhead when not profiling. + bool enableTracing = false; + // Helper to resolve output path for an artifact. // If outputDirectory is set, returns outputDirectory/filename. // Otherwise returns the original path unchanged. diff --git a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp index 2bb8595e0e..2bec7b85b6 100644 --- a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp +++ b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp @@ -15,6 +15,7 @@ #ifdef MATERIALX_BUILD_TRACING #include #include +#include #endif namespace mx = MaterialX; @@ -100,12 +101,16 @@ bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) } #ifdef MATERIALX_BUILD_TRACING - // Initialize tracing with target-specific trace filename - mx::FilePath tracePath = options.resolveOutputPath(_shaderGenerator->getTarget() + "_render_trace.perfetto-trace"); - mx::Tracing::Dispatcher::getInstance().setSink( - std::make_unique(tracePath.asString())); - // Scope guard ensures tracing is shut down on any exit path (return, exception, etc.) - mx::Tracing::Dispatcher::ShutdownGuard tracingGuard; + // Initialize tracing with target-specific trace filename (if enabled in options) + std::optional tracingGuard; + if (options.enableTracing) + { + mx::FilePath tracePath = options.resolveOutputPath(_shaderGenerator->getTarget() + "_render_trace.perfetto-trace"); + mx::Tracing::Dispatcher::getInstance().setSink( + std::make_unique(tracePath.asString())); + // Scope guard ensures tracing is shut down on any exit path (return, exception, etc.) + tracingGuard.emplace(); + } #endif #ifdef LOG_TO_FILE From b8cb9205d8c0cf3cf26dd0ea6855f04284ca250d Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Mon, 12 Jan 2026 15:15:05 -0500 Subject: [PATCH 14/40] Refactor tracing categories to use enum class - Replace namespace Category with constexpr strings -> enum class Category - Template-based Scope avoids storing category on stack - PerfettoSink uses switch statement (compiler optimizes to jump table) - Removed categoryMatches() string comparison overhead - Type-safe: can only pass valid Category enum values The enum-based design is simpler, more efficient, and equally compatible with a future USD TraceCollector sink (which uses integer category IDs, not strings). --- source/MaterialXCore/PerfettoSink.cpp | 96 +++++++++---------- source/MaterialXCore/PerfettoSink.h | 6 +- source/MaterialXCore/Tracing.h | 66 ++++++------- .../MaterialXRenderGlsl/RenderGlsl.cpp | 12 +-- 4 files changed, 89 insertions(+), 91 deletions(-) diff --git a/source/MaterialXCore/PerfettoSink.cpp b/source/MaterialXCore/PerfettoSink.cpp index 9b36a98a95..4f9b1aa676 100644 --- a/source/MaterialXCore/PerfettoSink.cpp +++ b/source/MaterialXCore/PerfettoSink.cpp @@ -8,7 +8,6 @@ #ifdef MATERIALX_BUILD_TRACING #include -#include #include // Define Perfetto trace categories for MaterialX @@ -78,65 +77,64 @@ PerfettoSink::~PerfettoSink() } } -// Helper to check category match (pointer comparison first for constexpr, then strcmp) -static bool categoryMatches(const char* category, const char* target) -{ - return category == target || (category && std::strcmp(category, target) == 0); -} - -void PerfettoSink::beginEvent(const char* category, const char* name) +void PerfettoSink::beginEvent(Category category, const char* name) { // Perfetto requires compile-time category names for TRACE_EVENT macros. - // We dispatch based on the runtime category to the appropriate compile-time macro. - // Pointer comparison catches constexpr usage, strcmp handles dynamic strings. - if (categoryMatches(category, Category::ShaderGen)) - { - TRACE_EVENT_BEGIN("mx.shadergen", nullptr, [&](perfetto::EventContext ctx) { - ctx.event()->set_name(name); - }); - } - else if (categoryMatches(category, Category::Optimize)) - { - TRACE_EVENT_BEGIN("mx.optimize", nullptr, [&](perfetto::EventContext ctx) { - ctx.event()->set_name(name); - }); - } - else if (categoryMatches(category, Category::Material)) + // Switch on the enum lets the compiler optimize to a jump table. + switch (category) { - TRACE_EVENT_BEGIN("mx.material", nullptr, [&](perfetto::EventContext ctx) { - ctx.event()->set_name(name); - }); - } - else // Default to mx.render - { - TRACE_EVENT_BEGIN("mx.render", nullptr, [&](perfetto::EventContext ctx) { - ctx.event()->set_name(name); - }); + case Category::Render: + TRACE_EVENT_BEGIN("mx.render", nullptr, [&](perfetto::EventContext ctx) { + ctx.event()->set_name(name); + }); + break; + case Category::ShaderGen: + TRACE_EVENT_BEGIN("mx.shadergen", nullptr, [&](perfetto::EventContext ctx) { + ctx.event()->set_name(name); + }); + break; + case Category::Optimize: + TRACE_EVENT_BEGIN("mx.optimize", nullptr, [&](perfetto::EventContext ctx) { + ctx.event()->set_name(name); + }); + break; + case Category::Material: + TRACE_EVENT_BEGIN("mx.material", nullptr, [&](perfetto::EventContext ctx) { + ctx.event()->set_name(name); + }); + break; + default: + // Fallback for any future categories + TRACE_EVENT_BEGIN("mx.render", nullptr, [&](perfetto::EventContext ctx) { + ctx.event()->set_name(name); + }); + break; } } -void PerfettoSink::endEvent(const char* category) +void PerfettoSink::endEvent(Category category) { - // Must match the category used in beginEvent - if (categoryMatches(category, Category::ShaderGen)) - { - TRACE_EVENT_END("mx.shadergen"); - } - else if (categoryMatches(category, Category::Optimize)) - { - TRACE_EVENT_END("mx.optimize"); - } - else if (categoryMatches(category, Category::Material)) - { - TRACE_EVENT_END("mx.material"); - } - else + switch (category) { - TRACE_EVENT_END("mx.render"); + case Category::Render: + TRACE_EVENT_END("mx.render"); + break; + case Category::ShaderGen: + TRACE_EVENT_END("mx.shadergen"); + break; + case Category::Optimize: + TRACE_EVENT_END("mx.optimize"); + break; + case Category::Material: + TRACE_EVENT_END("mx.material"); + break; + default: + TRACE_EVENT_END("mx.render"); + break; } } -void PerfettoSink::counter(const char* category, const char* name, double value) +void PerfettoSink::counter(Category category, const char* name, double value) { (void)category; // Create a counter track with the given name diff --git a/source/MaterialXCore/PerfettoSink.h b/source/MaterialXCore/PerfettoSink.h index 0ea0841eaf..5f74de14f7 100644 --- a/source/MaterialXCore/PerfettoSink.h +++ b/source/MaterialXCore/PerfettoSink.h @@ -81,9 +81,9 @@ class MX_CORE_API PerfettoSink : public Sink PerfettoSink& operator=(PerfettoSink&&) = delete; // Sink interface implementation - void beginEvent(const char* category, const char* name) override; - void endEvent(const char* category) override; - void counter(const char* category, const char* name, double value) override; + void beginEvent(Category category, const char* name) override; + void endEvent(Category category) override; + void counter(Category category, const char* name, double value) override; void setThreadName(const char* name) override; private: diff --git a/source/MaterialXCore/Tracing.h b/source/MaterialXCore/Tracing.h index b93d3cfafe..e948f4452e 100644 --- a/source/MaterialXCore/Tracing.h +++ b/source/MaterialXCore/Tracing.h @@ -16,11 +16,12 @@ /// - API similar to USD's TraceCollector/TraceScope for familiarity /// - Abstract sink allows USD to inject its own tracing when calling MaterialX /// - Zero overhead when tracing is disabled (macros compile to nothing) -/// - Constexpr category strings for compile-time validation +/// - Enum-based categories for type safety and efficient dispatch #include #include +#include #include #include @@ -31,30 +32,28 @@ MATERIALX_NAMESPACE_BEGIN namespace Tracing { -/// @namespace Category -/// Constexpr trace category identifiers. +/// @enum Category +/// Trace event categories for filtering and organization. /// -/// These are compile-time constant strings that identify trace event categories. -/// Using constexpr ensures type safety and enables compile-time validation. -/// The USD TraceCollector backend can map these to its own categories. -namespace Category +/// These are used to categorize trace events for filtering in the UI +/// and to enable/disable specific categories at runtime. +enum class Category { /// Rendering operations (GPU commands, frame capture, etc.) - constexpr const char* Render = "mx.render"; + Render = 0, /// Shader generation (code generation, optimization passes) - constexpr const char* ShaderGen = "mx.shadergen"; + ShaderGen, /// Optimization passes (constant folding, dead code elimination) - constexpr const char* Optimize = "mx.optimize"; + Optimize, /// Material/shader identity markers (for filtering/grouping in traces) - constexpr const char* Material = "mx.material"; -} - -// Usage: Add a namespace alias in your .cpp file for brevity: -// namespace trace = MaterialX::Tracing; -// MX_TRACE_SCOPE(trace::Category::Render, "MyEvent"); + Material, + + /// Number of categories (must be last) + Count +}; /// @class Sink /// Abstract tracing sink interface. @@ -67,13 +66,13 @@ class MX_CORE_API Sink virtual ~Sink() = default; /// Begin a trace event with the given category and name. - virtual void beginEvent(const char* category, const char* name) = 0; + virtual void beginEvent(Category category, const char* name) = 0; /// End the current trace event for the given category. - virtual void endEvent(const char* category) = 0; + virtual void endEvent(Category category) = 0; /// Record a counter value (e.g., GPU time, memory usage). - virtual void counter(const char* category, const char* name, double value) = 0; + virtual void counter(Category category, const char* name, double value) = 0; /// Set the current thread's name for trace visualization. virtual void setThreadName(const char* name) = 0; @@ -123,21 +122,21 @@ class MX_CORE_API Dispatcher bool isEnabled() const { return _sink != nullptr; } /// Begin a trace event. - void beginEvent(const char* category, const char* name) + void beginEvent(Category category, const char* name) { if (_sink) _sink->beginEvent(category, name); } /// End a trace event. - void endEvent(const char* category) + void endEvent(Category category) { if (_sink) _sink->endEvent(category); } /// Record a counter value. - void counter(const char* category, const char* name, double value) + void counter(Category category, const char* name, double value) { if (_sink) _sink->counter(category, name, value); @@ -154,31 +153,31 @@ class MX_CORE_API Dispatcher /// @class Scope /// RAII scope guard for trace events (similar to USD's TraceScope). /// +/// Template parameter Cat is the category enum value, known at compile time. +/// This avoids storing the category on the stack. +/// /// Usage: /// { -/// Tracing::Scope scope("mx.render", "RenderMaterial"); +/// Tracing::Scope scope("RenderMaterial"); /// // ... code to trace ... /// } // Event automatically ends here -class MX_CORE_API Scope +template +class Scope { public: - Scope(const char* category, const char* name) - : _category(category) + explicit Scope(const char* name) { - Dispatcher::getInstance().beginEvent(category, name); + Dispatcher::getInstance().beginEvent(Cat, name); } ~Scope() { - Dispatcher::getInstance().endEvent(_category); + Dispatcher::getInstance().endEvent(Cat); } // Non-copyable Scope(const Scope&) = delete; Scope& operator=(const Scope&) = delete; - - private: - const char* const _category; }; } // namespace Tracing @@ -198,12 +197,13 @@ MATERIALX_NAMESPACE_END #ifdef MATERIALX_BUILD_TRACING /// Create a scoped trace event. Event ends when scope exits. +/// Category must be a Tracing::Category enum value. #define MX_TRACE_SCOPE(category, name) \ - MaterialX::Tracing::Scope MX_TRACE_CONCAT(_mxTraceScope_, __LINE__)(category, name) + MaterialX::Tracing::Scope MX_TRACE_CONCAT(_mxTraceScope_, __LINE__)(name) /// Create a scoped trace event using the current function name. #define MX_TRACE_FUNCTION(category) \ - MaterialX::Tracing::Scope MX_TRACE_CONCAT(_mxTraceFn_, __LINE__)(category, __FUNCTION__) + MaterialX::Tracing::Scope MX_TRACE_CONCAT(_mxTraceFn_, __LINE__)(__FUNCTION__) /// Record a counter value. #define MX_TRACE_COUNTER(category, name, value) \ diff --git a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp index ba34f94118..33762f3040 100644 --- a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp +++ b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp @@ -20,7 +20,7 @@ #include namespace mx = MaterialX; -namespace cat = mx::Tracing::Category; +using Cat = mx::Tracing::Category; // // Render validation tester for the GLSL shading language @@ -163,8 +163,8 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, const std::string& outputPath, mx::ImageVec* imageVec) { - MX_TRACE_FUNCTION(cat::Render); - MX_TRACE_SCOPE(cat::Material, shaderName.c_str()); + MX_TRACE_FUNCTION(Cat::Render); + MX_TRACE_SCOPE(Cat::Material, shaderName.c_str()); std::cout << "Validating GLSL rendering for: " << doc->getSourceUri() << std::endl; mx::ScopedTimer totalGLSLTime(&profileTimes.languageTimes.totalTime); @@ -205,7 +205,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, transpTimer.endTimer(); mx::ScopedTimer generationTimer(&profileTimes.languageTimes.generationTime); - MX_TRACE_SCOPE(cat::ShaderGen, "GenerateShader"); + MX_TRACE_SCOPE(Cat::ShaderGen, "GenerateShader"); mx::GenOptions& contextOptions = context.getOptions(); contextOptions = options; contextOptions.targetColorSpaceOverride = "lin_rec709"; @@ -282,7 +282,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, _renderer->setLightHandler(isShader ? _lightHandler : nullptr); { - MX_TRACE_SCOPE(cat::Render, "CompileShader"); + MX_TRACE_SCOPE(Cat::Render, "CompileShader"); mx::ScopedTimer compileTimer(&profileTimes.languageTimes.compileTime); _renderer->createProgram(shader); _renderer->validateInputs(); @@ -349,7 +349,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, int supersampleFactor = testOptions.enableReferenceQuality ? 8 : 1; { - MX_TRACE_SCOPE(cat::Render, "RenderMaterial"); + MX_TRACE_SCOPE(Cat::Render, "RenderMaterial"); mx::ScopedTimer renderTimer(&profileTimes.languageTimes.renderTime); _renderer->getImageHandler()->setSearchPath(imageSearchPath); unsigned int width = (unsigned int) testOptions.renderSize[0] * supersampleFactor; From dc10eb677d5b129c14d94ccf06fea73f6337da4f Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Mon, 12 Jan 2026 15:27:21 -0500 Subject: [PATCH 15/40] Add category dispatch to PerfettoSink::counter() --- source/MaterialXCore/PerfettoSink.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/source/MaterialXCore/PerfettoSink.cpp b/source/MaterialXCore/PerfettoSink.cpp index 4f9b1aa676..942077160f 100644 --- a/source/MaterialXCore/PerfettoSink.cpp +++ b/source/MaterialXCore/PerfettoSink.cpp @@ -136,10 +136,25 @@ void PerfettoSink::endEvent(Category category) void PerfettoSink::counter(Category category, const char* name, double value) { - (void)category; - // Create a counter track with the given name auto track = perfetto::CounterTrack(name); - TRACE_COUNTER("mx.render", track, value); + switch (category) + { + case Category::Render: + TRACE_COUNTER("mx.render", track, value); + break; + case Category::ShaderGen: + TRACE_COUNTER("mx.shadergen", track, value); + break; + case Category::Optimize: + TRACE_COUNTER("mx.optimize", track, value); + break; + case Category::Material: + TRACE_COUNTER("mx.material", track, value); + break; + default: + TRACE_COUNTER("mx.render", track, value); + break; + } } void PerfettoSink::setThreadName(const char* name) From 46333411abca22808328052d1422a61ea0235698 Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Mon, 12 Jan 2026 15:33:06 -0500 Subject: [PATCH 16/40] Remove unused resolveOutputPathWithSubdir method --- .../MaterialXGenShader/GenShaderUtil.h | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h index b5683787c0..54b368ab23 100644 --- a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h +++ b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h @@ -139,30 +139,6 @@ class TestSuiteOptions return outputDirectory / path.getBaseName(); } - // Helper to resolve output path preserving subdirectory structure. - // If outputDirectory is set, returns outputDirectory/relativePath. - // Otherwise returns the original path unchanged. - mx::FilePath resolveOutputPathWithSubdir(const mx::FilePath& path, const mx::FilePath& baseDir) const - { - if (outputDirectory.isEmpty()) - { - return path; - } - // Try to make the path relative to baseDir to preserve structure - std::string pathStr = path.asString(); - std::string baseStr = baseDir.asString(); - if (pathStr.find(baseStr) == 0) - { - std::string relative = pathStr.substr(baseStr.length()); - if (!relative.empty() && (relative[0] == '/' || relative[0] == '\\')) - { - relative = relative.substr(1); - } - return outputDirectory / mx::FilePath(relative); - } - return outputDirectory / path.getBaseName(); - } - // Bake parameters struct BakeSetting { From d8e2f8df09143ec3e19065c15de19ae46bc3e1bf Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Thu, 15 Jan 2026 14:50:42 -0500 Subject: [PATCH 17/40] Move tracing to dedicated MaterialXTrace module Addresses PR feedback to create a dedicated module for tracing rather than adding it to MaterialXCore. Changes: - Create source/MaterialXTrace with Export.h, CMakeLists.txt - Move Tracing.h/cpp and PerfettoSink.h/cpp to MaterialXTrace - Update export macros from MX_CORE_API to MX_TRACE_API - Update include paths in test code - Link MaterialXTest against MaterialXTrace The MaterialXTrace library depends on MaterialXCore and contains all tracing infrastructure (Sink interface, Dispatcher singleton, PerfettoSink). This keeps MaterialXCore minimal for users who don't need tracing. --- CMakeLists.txt | 1 + source/MaterialXCore/CMakeLists.txt | 32 ------------ source/MaterialXTest/CMakeLists.txt | 1 + .../MaterialXRender/RenderUtil.cpp | 4 +- .../MaterialXRenderGlsl/RenderGlsl.cpp | 2 +- source/MaterialXTrace/CMakeLists.txt | 51 +++++++++++++++++++ source/MaterialXTrace/Export.h | 23 +++++++++ .../PerfettoSink.cpp | 2 +- .../PerfettoSink.h | 6 +-- .../Tracing.cpp | 2 +- .../Tracing.h | 6 +-- 11 files changed, 87 insertions(+), 43 deletions(-) create mode 100644 source/MaterialXTrace/CMakeLists.txt create mode 100644 source/MaterialXTrace/Export.h rename source/{MaterialXCore => MaterialXTrace}/PerfettoSink.cpp (99%) rename source/{MaterialXCore => MaterialXTrace}/PerfettoSink.h (95%) rename source/{MaterialXCore => MaterialXTrace}/Tracing.cpp (95%) rename source/{MaterialXCore => MaterialXTrace}/Tracing.h (98%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fca861018..44d7a51855 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -496,6 +496,7 @@ endif() # Add core subdirectories add_subdirectory(source/MaterialXCore) add_subdirectory(source/MaterialXFormat) +add_subdirectory(source/MaterialXTrace) # Add shader generation subdirectories add_subdirectory(source/MaterialXGenShader) diff --git a/source/MaterialXCore/CMakeLists.txt b/source/MaterialXCore/CMakeLists.txt index 980e37d6d6..c3c845afda 100644 --- a/source/MaterialXCore/CMakeLists.txt +++ b/source/MaterialXCore/CMakeLists.txt @@ -3,12 +3,6 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Generated.h.in ${CMAKE_CURRENT_BINARY file(GLOB materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") file(GLOB materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h" "${CMAKE_CURRENT_BINARY_DIR}/*.h") -# Exclude all tracing implementation when tracing is disabled (zero overhead) -if(NOT MATERIALX_BUILD_TRACING) - list(REMOVE_ITEM materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/Tracing.cpp") - list(REMOVE_ITEM materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/PerfettoSink.cpp") -endif() - mx_add_library(MaterialXCore SOURCE_FILES ${materialx_source} @@ -21,29 +15,3 @@ mx_add_library(MaterialXCore target_include_directories(${TARGET_NAME} PUBLIC $) - -# Perfetto tracing support -if(MATERIALX_BUILD_TRACING) - # The Perfetto SDK is distributed as an amalgamated single-file source (sdk/perfetto.cc). - # This is the official recommended integration method per Google's documentation: - # https://perfetto.dev/docs/instrumentation/tracing-sdk - # Compiling directly (vs. linking a library) simplifies integration and enables - # better compiler optimizations within a single translation unit. - target_sources(${TARGET_NAME} PRIVATE - "${perfetto_SOURCE_DIR}/sdk/perfetto.cc") - # Use generator expression - only needed at build time, not install - target_include_directories(${TARGET_NAME} PUBLIC - $) - target_compile_definitions(${TARGET_NAME} PUBLIC MATERIALX_BUILD_TRACING) - if(WIN32) - # ws2_32: Windows Sockets 2 library. Perfetto uses Winsock internally for - # inter-process communication (IPC), even in single-process tracing mode. - target_link_libraries(${TARGET_NAME} PRIVATE ws2_32) - # Prevent Windows.h min/max macros from breaking std::numeric_limits - # Also need /bigobj because perfetto.cc has too many sections for MSVC - set_source_files_properties("${perfetto_SOURCE_DIR}/sdk/perfetto.cc" - PROPERTIES - COMPILE_DEFINITIONS "NOMINMAX;WIN32_LEAN_AND_MEAN" - COMPILE_OPTIONS "/bigobj") - endif() -endif() diff --git a/source/MaterialXTest/CMakeLists.txt b/source/MaterialXTest/CMakeLists.txt index 01885adb1b..b351fef13e 100644 --- a/source/MaterialXTest/CMakeLists.txt +++ b/source/MaterialXTest/CMakeLists.txt @@ -40,6 +40,7 @@ add_subdirectory(MaterialXCore) target_link_libraries(MaterialXTest MaterialXCore) add_subdirectory(MaterialXFormat) target_link_libraries(MaterialXTest MaterialXFormat) +target_link_libraries(MaterialXTest MaterialXTrace) add_subdirectory(MaterialXGenShader) target_link_libraries(MaterialXTest MaterialXGenShader) diff --git a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp index 2bec7b85b6..2075200d52 100644 --- a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp +++ b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp @@ -13,8 +13,8 @@ #endif #ifdef MATERIALX_BUILD_TRACING -#include -#include +#include +#include #include #endif diff --git a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp index 33762f3040..66413d7304 100644 --- a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp +++ b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #if defined(MATERIALX_BUILD_OIIO) #include #endif diff --git a/source/MaterialXTrace/CMakeLists.txt b/source/MaterialXTrace/CMakeLists.txt new file mode 100644 index 0000000000..5ecab419c6 --- /dev/null +++ b/source/MaterialXTrace/CMakeLists.txt @@ -0,0 +1,51 @@ +# MaterialXTrace - Performance tracing infrastructure for MaterialX +# +# This module provides an abstract tracing interface that can be backed by +# different implementations (Perfetto, USD TraceCollector, etc.). + +file(GLOB materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") +file(GLOB materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h") + +# Exclude Perfetto sink when tracing is disabled +if(NOT MATERIALX_BUILD_TRACING) + list(REMOVE_ITEM materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/Tracing.cpp") + list(REMOVE_ITEM materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/PerfettoSink.cpp") + # Keep headers but they'll be no-ops via macros +endif() + +mx_add_library(MaterialXTrace + SOURCE_FILES + ${materialx_source} + HEADER_FILES + ${materialx_headers} + EXPORT_DEFINE + MATERIALX_TRACE_EXPORTS + LIBRARIES + MaterialXCore) + +# Perfetto tracing support +if(MATERIALX_BUILD_TRACING) + # The Perfetto SDK is distributed as an amalgamated single-file source (sdk/perfetto.cc). + # This is the official recommended integration method per Google's documentation: + # https://perfetto.dev/docs/instrumentation/tracing-sdk + # Compiling directly (vs. linking a library) simplifies integration and enables + # better compiler optimizations within a single translation unit. + target_sources(${TARGET_NAME} PRIVATE + "${perfetto_SOURCE_DIR}/sdk/perfetto.cc") + # Use generator expression - only needed at build time, not install + target_include_directories(${TARGET_NAME} PUBLIC + $) + target_compile_definitions(${TARGET_NAME} PUBLIC MATERIALX_BUILD_TRACING) + if(WIN32) + # ws2_32: Windows Sockets 2 library. Perfetto uses Winsock internally for + # inter-process communication (IPC), even in single-process tracing mode. + target_link_libraries(${TARGET_NAME} PRIVATE ws2_32) + # Prevent Windows.h min/max macros from breaking std::numeric_limits + # Also need /bigobj because perfetto.cc has too many sections for MSVC + set_source_files_properties("${perfetto_SOURCE_DIR}/sdk/perfetto.cc" + PROPERTIES + COMPILE_DEFINITIONS "NOMINMAX;WIN32_LEAN_AND_MEAN" + COMPILE_OPTIONS "/bigobj") + endif() +endif() + diff --git a/source/MaterialXTrace/Export.h b/source/MaterialXTrace/Export.h new file mode 100644 index 0000000000..5acf0fd357 --- /dev/null +++ b/source/MaterialXTrace/Export.h @@ -0,0 +1,23 @@ +// +// Copyright Contributors to the MaterialX Project +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef MATERIALX_TRACE_EXPORT_H +#define MATERIALX_TRACE_EXPORT_H + +#include + +/// @file +/// Macros for declaring imported and exported symbols. + +#if defined(MATERIALX_TRACE_EXPORTS) + #define MX_TRACE_API MATERIALX_SYMBOL_EXPORT + #define MX_TRACE_EXTERN_TEMPLATE(...) MATERIALX_EXPORT_EXTERN_TEMPLATE(__VA_ARGS__) +#else + #define MX_TRACE_API MATERIALX_SYMBOL_IMPORT + #define MX_TRACE_EXTERN_TEMPLATE(...) MATERIALX_IMPORT_EXTERN_TEMPLATE(__VA_ARGS__) +#endif + +#endif + diff --git a/source/MaterialXCore/PerfettoSink.cpp b/source/MaterialXTrace/PerfettoSink.cpp similarity index 99% rename from source/MaterialXCore/PerfettoSink.cpp rename to source/MaterialXTrace/PerfettoSink.cpp index 942077160f..7aa36998f5 100644 --- a/source/MaterialXCore/PerfettoSink.cpp +++ b/source/MaterialXTrace/PerfettoSink.cpp @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#include +#include #ifdef MATERIALX_BUILD_TRACING diff --git a/source/MaterialXCore/PerfettoSink.h b/source/MaterialXTrace/PerfettoSink.h similarity index 95% rename from source/MaterialXCore/PerfettoSink.h rename to source/MaterialXTrace/PerfettoSink.h index 5f74de14f7..49b1c6ccce 100644 --- a/source/MaterialXCore/PerfettoSink.h +++ b/source/MaterialXTrace/PerfettoSink.h @@ -10,7 +10,7 @@ /// Perfetto-based implementation of the Tracing::Sink interface. /// /// Usage: -/// #include +/// #include /// namespace trace = mx::Tracing; /// /// trace::Dispatcher::getInstance().setSink( @@ -27,7 +27,7 @@ /// // which writes the trace file to the path specified in constructor. /// // Open the .perfetto-trace file at https://ui.perfetto.dev -#include +#include #ifdef MATERIALX_BUILD_TRACING @@ -63,7 +63,7 @@ namespace Tracing /// /// Multiple PerfettoSink instances can coexist (each with its own session), /// though typically only one is active at a time via the Dispatcher. -class MX_CORE_API PerfettoSink : public Sink +class MX_TRACE_API PerfettoSink : public Sink { public: /// Construct and start a Perfetto tracing session. diff --git a/source/MaterialXCore/Tracing.cpp b/source/MaterialXTrace/Tracing.cpp similarity index 95% rename from source/MaterialXCore/Tracing.cpp rename to source/MaterialXTrace/Tracing.cpp index a3a9ebe1fe..75c00dcdd6 100644 --- a/source/MaterialXCore/Tracing.cpp +++ b/source/MaterialXTrace/Tracing.cpp @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#include +#include MATERIALX_NAMESPACE_BEGIN diff --git a/source/MaterialXCore/Tracing.h b/source/MaterialXTrace/Tracing.h similarity index 98% rename from source/MaterialXCore/Tracing.h rename to source/MaterialXTrace/Tracing.h index e948f4452e..6ac281e772 100644 --- a/source/MaterialXCore/Tracing.h +++ b/source/MaterialXTrace/Tracing.h @@ -18,7 +18,7 @@ /// - Zero overhead when tracing is disabled (macros compile to nothing) /// - Enum-based categories for type safety and efficient dispatch -#include +#include #include #include @@ -60,7 +60,7 @@ enum class Category /// /// Implementations can delegate to Perfetto, USD TraceCollector, or custom systems. /// This allows USD/Hydra to inject their own tracing when calling MaterialX code. -class MX_CORE_API Sink +class MX_TRACE_API Sink { public: virtual ~Sink() = default; @@ -88,7 +88,7 @@ class MX_CORE_API Sink /// Dispatcher::getInstance().setSink(std::make_unique(...)); /// // ... traced work ... /// Dispatcher::getInstance().shutdownSink(); -class MX_CORE_API Dispatcher +class MX_TRACE_API Dispatcher { public: /// Get the singleton instance. From b0d3003d2e2d322b585d090d4a54011dd07a16ed Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Thu, 15 Jan 2026 14:57:46 -0500 Subject: [PATCH 18/40] Only build MaterialXTrace when tracing is enabled Skip add_subdirectory(MaterialXTrace) and test linking when MATERIALX_BUILD_TRACING is OFF. This ensures zero overhead - no extra library built or linked when tracing is disabled. --- CMakeLists.txt | 4 +++- source/MaterialXTest/CMakeLists.txt | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 44d7a51855..acad1a6952 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -496,7 +496,9 @@ endif() # Add core subdirectories add_subdirectory(source/MaterialXCore) add_subdirectory(source/MaterialXFormat) -add_subdirectory(source/MaterialXTrace) +if(MATERIALX_BUILD_TRACING) + add_subdirectory(source/MaterialXTrace) +endif() # Add shader generation subdirectories add_subdirectory(source/MaterialXGenShader) diff --git a/source/MaterialXTest/CMakeLists.txt b/source/MaterialXTest/CMakeLists.txt index b351fef13e..659396d11c 100644 --- a/source/MaterialXTest/CMakeLists.txt +++ b/source/MaterialXTest/CMakeLists.txt @@ -40,7 +40,9 @@ add_subdirectory(MaterialXCore) target_link_libraries(MaterialXTest MaterialXCore) add_subdirectory(MaterialXFormat) target_link_libraries(MaterialXTest MaterialXFormat) -target_link_libraries(MaterialXTest MaterialXTrace) +if(MATERIALX_BUILD_TRACING) + target_link_libraries(MaterialXTest MaterialXTrace) +endif() add_subdirectory(MaterialXGenShader) target_link_libraries(MaterialXTest MaterialXGenShader) From 232e01f9d7d556e4ce9e0685a67e6dedb922b319 Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Thu, 15 Jan 2026 16:40:38 -0500 Subject: [PATCH 19/40] Enable tracing by default and in nightly CI - Set enableTracing default to true in _options.mtlx - Enable MATERIALX_BUILD_TRACING=ON for nightly/extended builds in GHA When MATERIALX_BUILD_TRACING is ON at build time and enableTracing is true at runtime, trace files will be generated during test runs. --- .github/workflows/main.yml | 2 ++ resources/Materials/TestSuite/_options.mtlx | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c7a16580e7..03d5fa771d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -206,6 +206,8 @@ jobs: if [ "${{ matrix.extended_build_mdl_sdk }}" == "ON" -a "${{ runner.os }}" == "Windows" ]; then EXTENDED_BUILD_CONFIG="$EXTENDED_BUILD_CONFIG -DMATERIALX_MDL_SDK_DIR=C:/vcpkg/installed/x64-windows" fi + # Enable Perfetto tracing for nightly builds + EXTENDED_BUILD_CONFIG="$EXTENDED_BUILD_CONFIG -DMATERIALX_BUILD_TRACING=ON" fi TEST_RENDER_CONFIG="-DMATERIALX_TEST_RENDER=OFF" if [ "${{ matrix.test_render }}" == "ON" ]; then diff --git a/resources/Materials/TestSuite/_options.mtlx b/resources/Materials/TestSuite/_options.mtlx index 95a7b80f25..49b0b7cc39 100644 --- a/resources/Materials/TestSuite/_options.mtlx +++ b/resources/Materials/TestSuite/_options.mtlx @@ -83,6 +83,6 @@ When enabled, generates .perfetto-trace files in outputDirectory. Default is false to avoid overhead when not profiling. --> - + From 2b1254b52c7bb473ee7a2ee6cad74ccc7ac1bfdf Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Thu, 15 Jan 2026 17:04:36 -0500 Subject: [PATCH 20/40] Add Perfetto trace artifact upload for extended builds Upload .perfetto-trace files from build/bin/ as artifacts when running extended builds (nightly/workflow_dispatch). This allows retrieving trace files for performance analysis. --- .github/workflows/main.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 03d5fa771d..3d1fb253fa 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -360,6 +360,14 @@ jobs: name: MaterialX_Coverage path: build/coverage + - name: Upload Perfetto Traces + uses: actions/upload-artifact@v4 + if: env.IS_EXTENDED_BUILD == 'true' + with: + name: Traces_${{ matrix.name }} + path: build/bin/*.perfetto-trace + if-no-files-found: ignore + javascript: name: JavaScript runs-on: ubuntu-latest From ffa4a77381aefadccead690fe16f3fc327763a0c Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Thu, 15 Jan 2026 17:24:19 -0500 Subject: [PATCH 21/40] Fix an ambiguous comment --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3d1fb253fa..792036f48b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -206,7 +206,7 @@ jobs: if [ "${{ matrix.extended_build_mdl_sdk }}" == "ON" -a "${{ runner.os }}" == "Windows" ]; then EXTENDED_BUILD_CONFIG="$EXTENDED_BUILD_CONFIG -DMATERIALX_MDL_SDK_DIR=C:/vcpkg/installed/x64-windows" fi - # Enable Perfetto tracing for nightly builds + # Enable Perfetto tracing EXTENDED_BUILD_CONFIG="$EXTENDED_BUILD_CONFIG -DMATERIALX_BUILD_TRACING=ON" fi TEST_RENDER_CONFIG="-DMATERIALX_TEST_RENDER=OFF" From a7dfa2fa901c3a475410379ce6dcc583848ecb52 Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Fri, 16 Jan 2026 08:31:08 -0500 Subject: [PATCH 22/40] Fix Linux build: link pthread for Perfetto SDK Perfetto SDK requires pthread on Linux/Unix for thread-local storage and synchronization. Add Threads::Threads dependency via CMake's FindThreads module. --- source/MaterialXTrace/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/MaterialXTrace/CMakeLists.txt b/source/MaterialXTrace/CMakeLists.txt index 5ecab419c6..5915bbb104 100644 --- a/source/MaterialXTrace/CMakeLists.txt +++ b/source/MaterialXTrace/CMakeLists.txt @@ -46,6 +46,10 @@ if(MATERIALX_BUILD_TRACING) PROPERTIES COMPILE_DEFINITIONS "NOMINMAX;WIN32_LEAN_AND_MEAN" COMPILE_OPTIONS "/bigobj") + elseif(UNIX) + # Perfetto requires pthread on Linux/Unix for thread-local storage and synchronization + find_package(Threads REQUIRED) + target_link_libraries(${TARGET_NAME} PRIVATE Threads::Threads) endif() endif() From 8b7e95d229c681773affb746859db1057e9b4f20 Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Fri, 16 Jan 2026 09:28:24 -0500 Subject: [PATCH 23/40] Fix MaterialXTrace CMake: use MTLX_MODULES instead of LIBRARIES The mx_add_library function expects MTLX_MODULES for dependencies, not LIBRARIES. --- source/MaterialXTrace/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/MaterialXTrace/CMakeLists.txt b/source/MaterialXTrace/CMakeLists.txt index 5915bbb104..8d10b3e9e5 100644 --- a/source/MaterialXTrace/CMakeLists.txt +++ b/source/MaterialXTrace/CMakeLists.txt @@ -20,7 +20,7 @@ mx_add_library(MaterialXTrace ${materialx_headers} EXPORT_DEFINE MATERIALX_TRACE_EXPORTS - LIBRARIES + MTLX_MODULES MaterialXCore) # Perfetto tracing support From baa407dc3a28936ade77c0127f61df9ba7a1ef1f Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Fri, 16 Jan 2026 09:42:27 -0500 Subject: [PATCH 24/40] Suppress warnings-as-errors for Perfetto SDK on Unix Perfetto SDK generates warnings that fail builds when MATERIALX_WARNINGS_AS_ERRORS is ON. Add -Wno-error for perfetto.cc on GCC/Clang. --- source/MaterialXTrace/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/MaterialXTrace/CMakeLists.txt b/source/MaterialXTrace/CMakeLists.txt index 8d10b3e9e5..c5d5fdb3c8 100644 --- a/source/MaterialXTrace/CMakeLists.txt +++ b/source/MaterialXTrace/CMakeLists.txt @@ -50,6 +50,11 @@ if(MATERIALX_BUILD_TRACING) # Perfetto requires pthread on Linux/Unix for thread-local storage and synchronization find_package(Threads REQUIRED) target_link_libraries(${TARGET_NAME} PRIVATE Threads::Threads) + # Suppress warnings from Perfetto SDK (they use patterns that trigger GCC/Clang warnings) + # This prevents build failures when MATERIALX_WARNINGS_AS_ERRORS is ON + set_source_files_properties("${perfetto_SOURCE_DIR}/sdk/perfetto.cc" + PROPERTIES + COMPILE_OPTIONS "-Wno-error") endif() endif() From 49e3ae2591c28c2927e9f1ff5a1f3078a6b29cbe Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Fri, 16 Jan 2026 09:46:41 -0500 Subject: [PATCH 25/40] Fix tracing build on GCC/Clang and iOS - Suppress warnings-as-errors for Perfetto SDK on Unix (GCC/Clang generate warnings that fail builds with MATERIALX_WARNINGS_AS_ERRORS) - Skip pthread linking on iOS (threads are built-in to iOS SDK) --- source/MaterialXTrace/CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/MaterialXTrace/CMakeLists.txt b/source/MaterialXTrace/CMakeLists.txt index c5d5fdb3c8..87bbed22a7 100644 --- a/source/MaterialXTrace/CMakeLists.txt +++ b/source/MaterialXTrace/CMakeLists.txt @@ -48,8 +48,11 @@ if(MATERIALX_BUILD_TRACING) COMPILE_OPTIONS "/bigobj") elseif(UNIX) # Perfetto requires pthread on Linux/Unix for thread-local storage and synchronization - find_package(Threads REQUIRED) - target_link_libraries(${TARGET_NAME} PRIVATE Threads::Threads) + # On iOS/macOS, threads are built-in to the system libraries + if(NOT CMAKE_SYSTEM_NAME STREQUAL "iOS") + find_package(Threads REQUIRED) + target_link_libraries(${TARGET_NAME} PRIVATE Threads::Threads) + endif() # Suppress warnings from Perfetto SDK (they use patterns that trigger GCC/Clang warnings) # This prevents build failures when MATERIALX_WARNINGS_AS_ERRORS is ON set_source_files_properties("${perfetto_SOURCE_DIR}/sdk/perfetto.cc" From 2bd72031c8b55a9d26a277c6312391e53e3d4de7 Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Fri, 16 Jan 2026 10:05:54 -0500 Subject: [PATCH 26/40] Fix CMake: use COMPILE_FLAGS instead of COMPILE_OPTIONS for source files COMPILE_OPTIONS is a target property. For set_source_files_properties, use COMPILE_FLAGS instead. This fixes /bigobj on Windows and -Wno-error on Unix. --- source/MaterialXTrace/CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/MaterialXTrace/CMakeLists.txt b/source/MaterialXTrace/CMakeLists.txt index 87bbed22a7..b7d84c99ce 100644 --- a/source/MaterialXTrace/CMakeLists.txt +++ b/source/MaterialXTrace/CMakeLists.txt @@ -42,10 +42,11 @@ if(MATERIALX_BUILD_TRACING) target_link_libraries(${TARGET_NAME} PRIVATE ws2_32) # Prevent Windows.h min/max macros from breaking std::numeric_limits # Also need /bigobj because perfetto.cc has too many sections for MSVC + # Note: Use COMPILE_FLAGS (not COMPILE_OPTIONS) for source file properties set_source_files_properties("${perfetto_SOURCE_DIR}/sdk/perfetto.cc" PROPERTIES COMPILE_DEFINITIONS "NOMINMAX;WIN32_LEAN_AND_MEAN" - COMPILE_OPTIONS "/bigobj") + COMPILE_FLAGS "/bigobj") elseif(UNIX) # Perfetto requires pthread on Linux/Unix for thread-local storage and synchronization # On iOS/macOS, threads are built-in to the system libraries @@ -55,9 +56,10 @@ if(MATERIALX_BUILD_TRACING) endif() # Suppress warnings from Perfetto SDK (they use patterns that trigger GCC/Clang warnings) # This prevents build failures when MATERIALX_WARNINGS_AS_ERRORS is ON + # Note: Use COMPILE_FLAGS (not COMPILE_OPTIONS) for source file properties set_source_files_properties("${perfetto_SOURCE_DIR}/sdk/perfetto.cc" PROPERTIES - COMPILE_OPTIONS "-Wno-error") + COMPILE_FLAGS "-Wno-error") endif() endif() From 4dba1fd87ed5ea723c1c416d3b0f8a7e2bc1b8bc Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Fri, 16 Jan 2026 10:17:44 -0500 Subject: [PATCH 27/40] Fix Perfetto symbol visibility for shared library builds - Define PERFETTO_COMPONENT_EXPORT for shared libs (required by Perfetto) - Add -fvisibility=default for perfetto.cc on Unix (fixes GCC 10 monolithic build) --- source/MaterialXTrace/CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/MaterialXTrace/CMakeLists.txt b/source/MaterialXTrace/CMakeLists.txt index b7d84c99ce..86ca898bf5 100644 --- a/source/MaterialXTrace/CMakeLists.txt +++ b/source/MaterialXTrace/CMakeLists.txt @@ -36,6 +36,10 @@ if(MATERIALX_BUILD_TRACING) target_include_directories(${TARGET_NAME} PUBLIC $) target_compile_definitions(${TARGET_NAME} PUBLIC MATERIALX_BUILD_TRACING) + # For shared library builds, Perfetto needs proper symbol visibility + if(BUILD_SHARED_LIBS OR MATERIALX_BUILD_SHARED_LIBS) + target_compile_definitions(${TARGET_NAME} PRIVATE PERFETTO_COMPONENT_EXPORT=MX_TRACE_API) + endif() if(WIN32) # ws2_32: Windows Sockets 2 library. Perfetto uses Winsock internally for # inter-process communication (IPC), even in single-process tracing mode. @@ -56,10 +60,11 @@ if(MATERIALX_BUILD_TRACING) endif() # Suppress warnings from Perfetto SDK (they use patterns that trigger GCC/Clang warnings) # This prevents build failures when MATERIALX_WARNINGS_AS_ERRORS is ON + # Also ensure proper symbol visibility for shared library builds # Note: Use COMPILE_FLAGS (not COMPILE_OPTIONS) for source file properties set_source_files_properties("${perfetto_SOURCE_DIR}/sdk/perfetto.cc" PROPERTIES - COMPILE_FLAGS "-Wno-error") + COMPILE_FLAGS "-Wno-error -fvisibility=default") endif() endif() From db914937dfebfd7d284b4f1ce2d70f58f7bea752 Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Fri, 16 Jan 2026 10:59:54 -0500 Subject: [PATCH 28/40] Isolate Perfetto SDK from DLL boundaries - Add createPerfettoSink() factory to Tracing.h (exported) - PerfettoSink class in PerfettoSink.h not exported (internal header) - Consumers include only Tracing.h and use factory function - Perfetto types never cross DLL boundaries - only abstract Sink does - Remove unnecessary PERFETTO_COMPONENT_EXPORT from CMakeLists.txt --- .../MaterialXRender/RenderUtil.cpp | 3 +- source/MaterialXTrace/CMakeLists.txt | 4 --- source/MaterialXTrace/Export.h | 1 + source/MaterialXTrace/PerfettoSink.cpp | 16 ++++++---- source/MaterialXTrace/PerfettoSink.h | 32 ++++++------------- source/MaterialXTrace/Tracing.h | 25 +++++++++++++++ 6 files changed, 46 insertions(+), 35 deletions(-) diff --git a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp index 2075200d52..f5e2198670 100644 --- a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp +++ b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp @@ -14,7 +14,6 @@ #ifdef MATERIALX_BUILD_TRACING #include -#include #include #endif @@ -107,7 +106,7 @@ bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) { mx::FilePath tracePath = options.resolveOutputPath(_shaderGenerator->getTarget() + "_render_trace.perfetto-trace"); mx::Tracing::Dispatcher::getInstance().setSink( - std::make_unique(tracePath.asString())); + mx::Tracing::createPerfettoSink(tracePath.asString())); // Scope guard ensures tracing is shut down on any exit path (return, exception, etc.) tracingGuard.emplace(); } diff --git a/source/MaterialXTrace/CMakeLists.txt b/source/MaterialXTrace/CMakeLists.txt index 86ca898bf5..78974eb3f5 100644 --- a/source/MaterialXTrace/CMakeLists.txt +++ b/source/MaterialXTrace/CMakeLists.txt @@ -36,10 +36,6 @@ if(MATERIALX_BUILD_TRACING) target_include_directories(${TARGET_NAME} PUBLIC $) target_compile_definitions(${TARGET_NAME} PUBLIC MATERIALX_BUILD_TRACING) - # For shared library builds, Perfetto needs proper symbol visibility - if(BUILD_SHARED_LIBS OR MATERIALX_BUILD_SHARED_LIBS) - target_compile_definitions(${TARGET_NAME} PRIVATE PERFETTO_COMPONENT_EXPORT=MX_TRACE_API) - endif() if(WIN32) # ws2_32: Windows Sockets 2 library. Perfetto uses Winsock internally for # inter-process communication (IPC), even in single-process tracing mode. diff --git a/source/MaterialXTrace/Export.h b/source/MaterialXTrace/Export.h index 5acf0fd357..00105df6ac 100644 --- a/source/MaterialXTrace/Export.h +++ b/source/MaterialXTrace/Export.h @@ -21,3 +21,4 @@ #endif + diff --git a/source/MaterialXTrace/PerfettoSink.cpp b/source/MaterialXTrace/PerfettoSink.cpp index 7aa36998f5..26b960ebed 100644 --- a/source/MaterialXTrace/PerfettoSink.cpp +++ b/source/MaterialXTrace/PerfettoSink.cpp @@ -31,21 +31,19 @@ MATERIALX_NAMESPACE_BEGIN namespace Tracing { -// One-time global Perfetto initialization flag -static std::once_flag g_perfettoInitFlag; - PerfettoSink::PerfettoSink(std::string outputPath, size_t bufferSizeKb) : _outputPath(std::move(outputPath)) { - // One-time global Perfetto initialization (safe to call from multiple instances) - std::call_once(g_perfettoInitFlag, []() { + // One-time global Perfetto initialization + static std::once_flag initFlag; + std::call_once(initFlag, []() { perfetto::TracingInitArgs args; args.backends |= perfetto::kInProcessBackend; perfetto::Tracing::Initialize(args); perfetto::TrackEvent::Register(); }); - // Create and start a tracing session for this sink + // Create and start a tracing session perfetto::TraceConfig cfg; cfg.add_buffers()->set_size_kb(static_cast(bufferSizeKb)); @@ -166,6 +164,12 @@ void PerfettoSink::setThreadName(const char* name) perfetto::TrackEvent::SetTrackDescriptor(track, desc); } +// Factory function - the exported entry point +std::unique_ptr createPerfettoSink(const std::string& outputPath, size_t bufferSizeKb) +{ + return std::make_unique(outputPath, bufferSizeKb); +} + } // namespace Tracing MATERIALX_NAMESPACE_END diff --git a/source/MaterialXTrace/PerfettoSink.h b/source/MaterialXTrace/PerfettoSink.h index 49b1c6ccce..c2459c4629 100644 --- a/source/MaterialXTrace/PerfettoSink.h +++ b/source/MaterialXTrace/PerfettoSink.h @@ -9,23 +9,11 @@ /// @file /// Perfetto-based implementation of the Tracing::Sink interface. /// -/// Usage: -/// #include -/// namespace trace = mx::Tracing; +/// This header is internal to MaterialXTrace. Users should NOT include it +/// directly. Instead, use the createPerfettoSink() factory in Tracing.h: /// -/// trace::Dispatcher::getInstance().setSink( -/// std::make_unique("trace.perfetto-trace")); -/// -/// // Use a local scope guard for exception safety -/// struct SinkGuard { -/// ~SinkGuard() { trace::Dispatcher::getInstance().shutdownSink(); } -/// } guard; -/// -/// // ... traced work ... -/// -/// // guard destructor calls shutdownSink(), which destroys PerfettoSink, -/// // which writes the trace file to the path specified in constructor. -/// // Open the .perfetto-trace file at https://ui.perfetto.dev +/// #include +/// auto sink = mx::Tracing::createPerfettoSink("trace.perfetto-trace"); #include @@ -56,14 +44,12 @@ namespace Tracing /// @class PerfettoSink /// Perfetto-based implementation of Tracing::Sink. /// -/// This class provides a concrete implementation using Perfetto SDK's -/// in-process tracing backend. The constructor starts a tracing session, -/// and the destructor stops the session and writes trace data to a -/// .perfetto-trace file that can be visualized at https://ui.perfetto.dev +/// Uses Perfetto SDK's in-process tracing backend. The constructor starts +/// a tracing session, and the destructor stops it and writes trace data +/// to a .perfetto-trace file viewable at https://ui.perfetto.dev /// -/// Multiple PerfettoSink instances can coexist (each with its own session), -/// though typically only one is active at a time via the Dispatcher. -class MX_TRACE_API PerfettoSink : public Sink +/// @note Do not use this class directly. Use createPerfettoSink() factory. +class PerfettoSink : public Sink { public: /// Construct and start a Perfetto tracing session. diff --git a/source/MaterialXTrace/Tracing.h b/source/MaterialXTrace/Tracing.h index 6ac281e772..f76070b00f 100644 --- a/source/MaterialXTrace/Tracing.h +++ b/source/MaterialXTrace/Tracing.h @@ -180,6 +180,31 @@ class Scope Scope& operator=(const Scope&) = delete; }; +// ============================================================================ +// Sink Factory Functions +// ============================================================================ + +#ifdef MATERIALX_BUILD_TRACING + +/// Create a Perfetto-based tracing sink. +/// +/// The returned sink writes trace data to a .perfetto-trace file that can be +/// visualized at https://ui.perfetto.dev +/// +/// @param outputPath Path to write the trace file when the sink is destroyed +/// @param bufferSizeKb Size of the trace buffer in KB (default 32MB) +/// @return A unique_ptr to the Perfetto sink +/// +/// Usage: +/// Dispatcher::getInstance().setSink(createPerfettoSink("trace.perfetto-trace")); +/// Dispatcher::ShutdownGuard guard; +/// // ... traced work ... +/// // guard destructor writes the trace file +MX_TRACE_API std::unique_ptr createPerfettoSink( + const std::string& outputPath, size_t bufferSizeKb = 32768); + +#endif // MATERIALX_BUILD_TRACING + } // namespace Tracing MATERIALX_NAMESPACE_END From cf7d434a6922f0037adb90f39cbfbe610a164012 Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Fri, 16 Jan 2026 11:21:22 -0500 Subject: [PATCH 29/40] Fix Perfetto SDK compile flags for all build configurations - Define MATERIALX_PERFETTO_COMPILE_DEFINITIONS and MATERIALX_PERFETTO_COMPILE_FLAGS as CACHE INTERNAL variables in root CMakeLists.txt - Apply flags in both root (for monolithic builds) and MaterialXTrace (for non-monolithic) - set_source_files_properties is directory-scoped, so must be called in both places - Fixes -Werror=shadow on GCC (kDevNull shadowing) - Fixes min/max macro conflicts on Windows (NOMINMAX) --- CMakeLists.txt | 19 +++++++++++ source/MaterialXTrace/CMakeLists.txt | 48 ++++++++++++++-------------- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index acad1a6952..94898aff36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -243,6 +243,25 @@ if(MATERIALX_BUILD_TRACING) set(PERFETTO_SDK_INCLUDE_DIR "${CMAKE_BINARY_DIR}/_deps/perfetto-src/sdk") FetchContent_MakeAvailable(perfetto) add_definitions(-DMATERIALX_BUILD_TRACING) + + # Define compile flags for perfetto.cc (Perfetto SDK triggers various warnings) + # These variables are used here for monolithic builds and in MaterialXTrace for non-monolithic + if(MSVC) + set(MATERIALX_PERFETTO_COMPILE_DEFINITIONS "NOMINMAX;WIN32_LEAN_AND_MEAN" CACHE INTERNAL "") + set(MATERIALX_PERFETTO_COMPILE_FLAGS "/bigobj" CACHE INTERNAL "") + else() + set(MATERIALX_PERFETTO_COMPILE_DEFINITIONS "" CACHE INTERNAL "") + # -Wno-error: Don't treat warnings as errors (Perfetto has shadowing issues) + # -Wno-shadow: Suppress shadow warnings (kDevNull shadows global) + set(MATERIALX_PERFETTO_COMPILE_FLAGS "-Wno-error -Wno-shadow" CACHE INTERNAL "") + endif() + + # Apply flags here for monolithic builds (set_source_files_properties is directory-scoped) + set_source_files_properties("${perfetto_SOURCE_DIR}/sdk/perfetto.cc" + PROPERTIES + COMPILE_DEFINITIONS "${MATERIALX_PERFETTO_COMPILE_DEFINITIONS}" + COMPILE_FLAGS "${MATERIALX_PERFETTO_COMPILE_FLAGS}") + message(STATUS "Perfetto tracing support enabled") endif() diff --git a/source/MaterialXTrace/CMakeLists.txt b/source/MaterialXTrace/CMakeLists.txt index 78974eb3f5..d31f3d5a70 100644 --- a/source/MaterialXTrace/CMakeLists.txt +++ b/source/MaterialXTrace/CMakeLists.txt @@ -4,7 +4,12 @@ # different implementations (Perfetto, USD TraceCollector, etc.). file(GLOB materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") -file(GLOB materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h") + +# Public headers only - PerfettoSink.h is internal (includes Perfetto SDK headers) +set(materialx_headers + ${CMAKE_CURRENT_SOURCE_DIR}/Export.h + ${CMAKE_CURRENT_SOURCE_DIR}/Tracing.h +) # Exclude Perfetto sink when tracing is disabled if(NOT MATERIALX_BUILD_TRACING) @@ -30,37 +35,32 @@ if(MATERIALX_BUILD_TRACING) # https://perfetto.dev/docs/instrumentation/tracing-sdk # Compiling directly (vs. linking a library) simplifies integration and enables # better compiler optimizations within a single translation unit. + # + # NOTE: Compile flags for perfetto.cc are set in root CMakeLists.txt so they apply + # to all targets (including monolithic builds which aggregate sources). target_sources(${TARGET_NAME} PRIVATE "${perfetto_SOURCE_DIR}/sdk/perfetto.cc") # Use generator expression - only needed at build time, not install target_include_directories(${TARGET_NAME} PUBLIC $) target_compile_definitions(${TARGET_NAME} PUBLIC MATERIALX_BUILD_TRACING) + + # Platform-specific link libraries if(WIN32) - # ws2_32: Windows Sockets 2 library. Perfetto uses Winsock internally for - # inter-process communication (IPC), even in single-process tracing mode. + # ws2_32: Windows Sockets 2 library for Perfetto IPC target_link_libraries(${TARGET_NAME} PRIVATE ws2_32) - # Prevent Windows.h min/max macros from breaking std::numeric_limits - # Also need /bigobj because perfetto.cc has too many sections for MSVC - # Note: Use COMPILE_FLAGS (not COMPILE_OPTIONS) for source file properties - set_source_files_properties("${perfetto_SOURCE_DIR}/sdk/perfetto.cc" - PROPERTIES - COMPILE_DEFINITIONS "NOMINMAX;WIN32_LEAN_AND_MEAN" - COMPILE_FLAGS "/bigobj") - elseif(UNIX) - # Perfetto requires pthread on Linux/Unix for thread-local storage and synchronization - # On iOS/macOS, threads are built-in to the system libraries - if(NOT CMAKE_SYSTEM_NAME STREQUAL "iOS") - find_package(Threads REQUIRED) - target_link_libraries(${TARGET_NAME} PRIVATE Threads::Threads) - endif() - # Suppress warnings from Perfetto SDK (they use patterns that trigger GCC/Clang warnings) - # This prevents build failures when MATERIALX_WARNINGS_AS_ERRORS is ON - # Also ensure proper symbol visibility for shared library builds - # Note: Use COMPILE_FLAGS (not COMPILE_OPTIONS) for source file properties - set_source_files_properties("${perfetto_SOURCE_DIR}/sdk/perfetto.cc" - PROPERTIES - COMPILE_FLAGS "-Wno-error -fvisibility=default") + elseif(UNIX AND NOT CMAKE_SYSTEM_NAME STREQUAL "iOS") + # Perfetto requires pthread on Linux/Unix (but not iOS where it's built-in) + find_package(Threads REQUIRED) + target_link_libraries(${TARGET_NAME} PRIVATE Threads::Threads) endif() + + # Apply Perfetto compile flags (defined in root CMakeLists.txt) + # NOTE: set_source_files_properties is directory-scoped, so we must set them + # here for non-monolithic builds AND in root CMakeLists.txt for monolithic builds. + set_source_files_properties("${perfetto_SOURCE_DIR}/sdk/perfetto.cc" + PROPERTIES + COMPILE_DEFINITIONS "${MATERIALX_PERFETTO_COMPILE_DEFINITIONS}" + COMPILE_FLAGS "${MATERIALX_PERFETTO_COMPILE_FLAGS}") endif() From b5c32bf5a7661d3ad0d55169b8c6aa497fdf00a8 Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Fri, 16 Jan 2026 11:34:01 -0500 Subject: [PATCH 30/40] Fix Perfetto compile flags for monolithic builds set_source_files_properties is directory-scoped, so: - Root CMakeLists.txt: defines MATERIALX_PERFETTO_* variables - source/CMakeLists.txt: applies flags for monolithic builds - MaterialXTrace/CMakeLists.txt: applies flags for non-monolithic builds --- CMakeLists.txt | 10 +++------- source/CMakeLists.txt | 9 +++++++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 94898aff36..4ef9d299a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -245,7 +245,9 @@ if(MATERIALX_BUILD_TRACING) add_definitions(-DMATERIALX_BUILD_TRACING) # Define compile flags for perfetto.cc (Perfetto SDK triggers various warnings) - # These variables are used here for monolithic builds and in MaterialXTrace for non-monolithic + # These variables are used in source/CMakeLists.txt (monolithic) and MaterialXTrace (non-monolithic) + # Note: set_source_files_properties is directory-scoped, so we define variables here + # but apply them in the directories where targets are created. if(MSVC) set(MATERIALX_PERFETTO_COMPILE_DEFINITIONS "NOMINMAX;WIN32_LEAN_AND_MEAN" CACHE INTERNAL "") set(MATERIALX_PERFETTO_COMPILE_FLAGS "/bigobj" CACHE INTERNAL "") @@ -256,12 +258,6 @@ if(MATERIALX_BUILD_TRACING) set(MATERIALX_PERFETTO_COMPILE_FLAGS "-Wno-error -Wno-shadow" CACHE INTERNAL "") endif() - # Apply flags here for monolithic builds (set_source_files_properties is directory-scoped) - set_source_files_properties("${perfetto_SOURCE_DIR}/sdk/perfetto.cc" - PROPERTIES - COMPILE_DEFINITIONS "${MATERIALX_PERFETTO_COMPILE_DEFINITIONS}" - COMPILE_FLAGS "${MATERIALX_PERFETTO_COMPILE_FLAGS}") - message(STATUS "Perfetto tracing support enabled") endif() diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index e1cd929ff9..b0f3a31927 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -5,6 +5,15 @@ if (MATERIALX_BUILD_MONOLITHIC) # such that the individual module would have been built if not in monolithic build mode add_library(${MATERIALX_MODULE_NAME} "" "" ) + # Apply Perfetto compile flags for monolithic builds + # (set_source_files_properties is directory-scoped, must be called here) + if(MATERIALX_BUILD_TRACING) + set_source_files_properties("${perfetto_SOURCE_DIR}/sdk/perfetto.cc" + PROPERTIES + COMPILE_DEFINITIONS "${MATERIALX_PERFETTO_COMPILE_DEFINITIONS}" + COMPILE_FLAGS "${MATERIALX_PERFETTO_COMPILE_FLAGS}") + endif() + set_target_properties(${MATERIALX_MODULE_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden) set_target_properties(${MATERIALX_MODULE_NAME} PROPERTIES CMAKE_VISIBILITY_INLINES_HIDDEN 1) From fce8654f6543ab69b944dffde673052f2efdefdd Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Fri, 16 Jan 2026 14:13:09 -0500 Subject: [PATCH 31/40] Exclude Perfetto SDK from coverage analysis perfetto.cc is ~160k lines and generates gcov output that gcovr cannot parse. Exclude it from coverage since it's third-party code anyway. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 792036f48b..912fd45712 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -268,7 +268,7 @@ jobs: run: | sudo apt-get install gcovr mkdir coverage - gcovr --html --html-details --output coverage/index.html --exclude .*\/External\/.* --root .. . + gcovr --html --html-details --output coverage/index.html --exclude .*\/External\/.* --exclude .*perfetto.* --root .. . working-directory: build - name: Static Analysis Tests From 9db6ef1af2f90da503d72740aab9cceb9f0ced3e Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Fri, 16 Jan 2026 14:15:12 -0500 Subject: [PATCH 32/40] Exclude Perfetto SDK from coverage, static analysis, and suppress MSVC warnings - Add --exclude .*perfetto.* to gcovr (coverage) - perfetto.cc is ~160k lines and generates gcov output that gcovr cannot parse - Add --suppress=*:*perfetto* to cppcheck (static analysis) - cppcheck doesn't define OS macros needed by Perfetto SDK's platform detection - Use /W0 to disable all warnings for Perfetto SDK on MSVC (third-party code triggers C4146, C4369, C4996, C4459, C4065, C4244, C4267, C4293) --- .github/workflows/main.yml | 2 +- CMakeLists.txt | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 912fd45712..50d6a467c6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -279,7 +279,7 @@ jobs: else brew install cppcheck fi - cppcheck --project=build/compile_commands.json --error-exitcode=1 --suppress=normalCheckLevelMaxBranches --suppress=*:*/External/* --suppress=*:*/NanoGUI/* + cppcheck --project=build/compile_commands.json --error-exitcode=1 --suppress=normalCheckLevelMaxBranches --suppress=*:*/External/* --suppress=*:*/NanoGUI/* --suppress=*:*perfetto* - name: Setup Rendering Environment (Linux) if: matrix.test_render == 'ON' && runner.os == 'Linux' diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ef9d299a6..1c6d074628 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -250,7 +250,9 @@ if(MATERIALX_BUILD_TRACING) # but apply them in the directories where targets are created. if(MSVC) set(MATERIALX_PERFETTO_COMPILE_DEFINITIONS "NOMINMAX;WIN32_LEAN_AND_MEAN" CACHE INTERNAL "") - set(MATERIALX_PERFETTO_COMPILE_FLAGS "/bigobj" CACHE INTERNAL "") + # /bigobj: perfetto.cc has too many sections for default MSVC object format + # /W0: disable all warnings for third-party Perfetto SDK code + set(MATERIALX_PERFETTO_COMPILE_FLAGS "/bigobj /W0" CACHE INTERNAL "") else() set(MATERIALX_PERFETTO_COMPILE_DEFINITIONS "" CACHE INTERNAL "") # -Wno-error: Don't treat warnings as errors (Perfetto has shadowing issues) From 41fe787e5f4d64a71ec8d3c2d2519e7546906c4b Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Fri, 16 Jan 2026 14:42:25 -0500 Subject: [PATCH 33/40] Add /WX- to disable warnings-as-errors for Perfetto SDK on MSVC /W0 alone doesn't override project-level /WX. Need /WX- explicitly to prevent STL header warnings (from tuple, etc.) being promoted to errors when compiling perfetto.cc. --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c6d074628..66cdc079ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -252,7 +252,8 @@ if(MATERIALX_BUILD_TRACING) set(MATERIALX_PERFETTO_COMPILE_DEFINITIONS "NOMINMAX;WIN32_LEAN_AND_MEAN" CACHE INTERNAL "") # /bigobj: perfetto.cc has too many sections for default MSVC object format # /W0: disable all warnings for third-party Perfetto SDK code - set(MATERIALX_PERFETTO_COMPILE_FLAGS "/bigobj /W0" CACHE INTERNAL "") + # /WX-: disable warnings-as-errors (overrides project-level /WX) + set(MATERIALX_PERFETTO_COMPILE_FLAGS "/bigobj /W0 /WX-" CACHE INTERNAL "") else() set(MATERIALX_PERFETTO_COMPILE_DEFINITIONS "" CACHE INTERNAL "") # -Wno-error: Don't treat warnings as errors (Perfetto has shadowing issues) From 7367222c63e1ef6e8410c91e0ee304fd2ff1668d Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Fri, 16 Jan 2026 15:20:53 -0500 Subject: [PATCH 34/40] Fix Perfetto trace artifact upload path Use recursive glob build/**/*.perfetto-trace since traces are written to current working directory (build/) not build/bin/ --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 50d6a467c6..9065d28a43 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -365,7 +365,7 @@ jobs: if: env.IS_EXTENDED_BUILD == 'true' with: name: Traces_${{ matrix.name }} - path: build/bin/*.perfetto-trace + path: build/**/*.perfetto-trace if-no-files-found: ignore javascript: From 361eec63438c5aa5c55d63d8fee3abe21cbe6534 Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Fri, 16 Jan 2026 15:43:21 -0500 Subject: [PATCH 35/40] Add tracing instrumentation to shader generation tests - Set up Perfetto sink in ShaderGeneratorTester::validate() when enableTracing=true - Add MX_TRACE_SCOPE around generateCode() calls with element names - Generates _gen_trace.perfetto-trace files (e.g. genglsl_gen_trace.perfetto-trace) - These tests run on all CI platforms, providing trace artifacts for download --- .../MaterialXGenShader/GenShaderUtil.cpp | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp index 0ef9d544b3..1212421140 100644 --- a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp +++ b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp @@ -23,7 +23,10 @@ #include #include +#include + #include +#include namespace mx = MaterialX; @@ -666,6 +669,18 @@ void ShaderGeneratorTester::validate(const mx::GenOptions& generateOptions, cons return; } +#ifdef MATERIALX_BUILD_TRACING + // Set up Perfetto tracing if enabled + std::optional tracingGuard; + if (options.enableTracing) + { + mx::FilePath tracePath = options.resolveOutputPath(_shaderGenerator->getTarget() + "_gen_trace.perfetto-trace"); + mx::Tracing::Dispatcher::getInstance().setSink( + mx::Tracing::createPerfettoSink(tracePath.asString())); + tracingGuard.emplace(); + } +#endif + // Start logging - use outputDirectory if set mx::FilePath logPath = options.resolveOutputPath(_logFilePath); _logFile.open(logPath.asString()); @@ -839,7 +854,11 @@ void ShaderGeneratorTester::validate(const mx::GenOptions& generateOptions, cons _logFile << "------------ Run validation with element: " << namePath << "------------" << std::endl; mx::StringVec sourceCode; - const bool generatedCode = generateCode(context, elementName, element, _logFile, _testStages, sourceCode); + bool generatedCode = false; + { + MX_TRACE_SCOPE(mx::Tracing::Category::ShaderGen, elementName.c_str()); + generatedCode = generateCode(context, elementName, element, _logFile, _testStages, sourceCode); + } // Record implementations tested if (options.checkImplCount) From b927f2134ebb774dc65b09235f6c3fd3aee163de Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Thu, 22 Jan 2026 16:13:24 -0500 Subject: [PATCH 36/40] Fix /reduced subdirectory creation when outputDirectory is set When outputDirectory is specified and shaderInterfaces=REDUCED, the render tests were failing to create the parent material directory before attempting to create the /reduced subdirectory, causing shader dumps to fail. The fix ensures createDirectory() is called on the parent outputPath first, then on outputPath/reduced if in REDUCED mode. Both calls are now within the same ScopedTimer scope for consistent profiling. Fixed in all four render test implementations: - RenderGlsl.cpp - RenderOsl.cpp - RenderMsl.mm - RenderSlang.cpp --- .../MaterialXRenderGlsl/RenderGlsl.cpp | 14 ++++++++------ .../MaterialXTest/MaterialXRenderMsl/RenderMsl.mm | 14 ++++++++------ .../MaterialXTest/MaterialXRenderOsl/RenderOsl.cpp | 14 ++++++++------ .../MaterialXRenderSlang/RenderSlang.cpp | 14 ++++++++------ 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp index 66413d7304..c443b25d66 100644 --- a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp +++ b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp @@ -184,16 +184,18 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, profileTimes.elementsTested++; mx::FilePath outputFilePath = outputPath; - // Use separate directory for reduced output - if (options.shaderInterfaceType == mx::SHADER_INTERFACE_REDUCED) - { - outputFilePath = outputFilePath / mx::FilePath("reduced"); - } - + // Note: mkdir will fail if the directory already exists which is ok. { mx::ScopedTimer ioDir(&profileTimes.languageTimes.ioTime); outputFilePath.createDirectory(); + + // Use separate directory for reduced output + if (options.shaderInterfaceType == mx::SHADER_INTERFACE_REDUCED) + { + outputFilePath = outputFilePath / mx::FilePath("reduced"); + outputFilePath.createDirectory(); + } } std::string shaderPath = mx::FilePath(outputFilePath) / mx::FilePath(shaderName); diff --git a/source/MaterialXTest/MaterialXRenderMsl/RenderMsl.mm b/source/MaterialXTest/MaterialXRenderMsl/RenderMsl.mm index 82ff81b96a..c6340101da 100644 --- a/source/MaterialXTest/MaterialXRenderMsl/RenderMsl.mm +++ b/source/MaterialXTest/MaterialXRenderMsl/RenderMsl.mm @@ -188,16 +188,18 @@ bool runRenderer(const std::string& shaderName, profileTimes.elementsTested++; mx::FilePath outputFilePath = outputPath; - // Use separate directory for reduced output - if (options.shaderInterfaceType == mx::SHADER_INTERFACE_REDUCED) - { - outputFilePath = outputFilePath / mx::FilePath("reduced"); - } - + // Note: mkdir will fail if the directory already exists which is ok. { mx::ScopedTimer ioDir(&profileTimes.languageTimes.ioTime); outputFilePath.createDirectory(); + + // Use separate directory for reduced output + if (options.shaderInterfaceType == mx::SHADER_INTERFACE_REDUCED) + { + outputFilePath = outputFilePath / mx::FilePath("reduced"); + outputFilePath.createDirectory(); + } } std::string shaderPath = mx::FilePath(outputFilePath) / mx::FilePath(shaderName); diff --git a/source/MaterialXTest/MaterialXRenderOsl/RenderOsl.cpp b/source/MaterialXTest/MaterialXRenderOsl/RenderOsl.cpp index 42ab9aee9c..6b92838d34 100644 --- a/source/MaterialXTest/MaterialXRenderOsl/RenderOsl.cpp +++ b/source/MaterialXTest/MaterialXRenderOsl/RenderOsl.cpp @@ -256,16 +256,18 @@ bool OslShaderRenderTester::runRenderer(const std::string& shaderName, std::string shaderPath; mx::FilePath outputFilePath = outputPath; - // Use separate directory for reduced output - if (options.shaderInterfaceType == mx::SHADER_INTERFACE_REDUCED) - { - outputFilePath = outputFilePath / mx::FilePath("reduced"); - } - + // Note: mkdir will fail if the directory already exists which is ok. { mx::ScopedTimer ioDir(&profileTimes.languageTimes.ioTime); outputFilePath.createDirectory(); + + // Use separate directory for reduced output + if (options.shaderInterfaceType == mx::SHADER_INTERFACE_REDUCED) + { + outputFilePath = outputFilePath / mx::FilePath("reduced"); + outputFilePath.createDirectory(); + } } shaderPath = mx::FilePath(outputFilePath) / mx::FilePath(shaderName); diff --git a/source/MaterialXTest/MaterialXRenderSlang/RenderSlang.cpp b/source/MaterialXTest/MaterialXRenderSlang/RenderSlang.cpp index 4d47cad68b..7a96b7af78 100644 --- a/source/MaterialXTest/MaterialXRenderSlang/RenderSlang.cpp +++ b/source/MaterialXTest/MaterialXRenderSlang/RenderSlang.cpp @@ -180,16 +180,18 @@ bool SlangShaderRenderTester::runRenderer(const std::string& shaderName, profileTimes.elementsTested++; mx::FilePath outputFilePath = outputPath; - // Use separate directory for reduced output - if (options.shaderInterfaceType == mx::SHADER_INTERFACE_REDUCED) - { - outputFilePath = outputFilePath / mx::FilePath("reduced"); - } - + // Note: mkdir will fail if the directory already exists which is ok. { mx::ScopedTimer ioDir(&profileTimes.languageTimes.ioTime); outputFilePath.createDirectory(); + + // Use separate directory for reduced output + if (options.shaderInterfaceType == mx::SHADER_INTERFACE_REDUCED) + { + outputFilePath = outputFilePath / mx::FilePath("reduced"); + outputFilePath.createDirectory(); + } } std::string shaderPath = mx::FilePath(outputFilePath) / mx::FilePath(shaderName); From 8af946e2dc5a0447d9be261b6c0aed8befeae334 Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Thu, 12 Feb 2026 16:10:37 -0500 Subject: [PATCH 37/40] Use fully qualified mx::Tracing::Category in RenderGlsl.cpp Address review: remove `using Cat` alias and spell out the full mx::Tracing::Category names for clarity while the Tracing library is still new. --- .../MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp index c443b25d66..197c43c93e 100644 --- a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp +++ b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp @@ -20,7 +20,6 @@ #include namespace mx = MaterialX; -using Cat = mx::Tracing::Category; // // Render validation tester for the GLSL shading language @@ -163,8 +162,8 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, const std::string& outputPath, mx::ImageVec* imageVec) { - MX_TRACE_FUNCTION(Cat::Render); - MX_TRACE_SCOPE(Cat::Material, shaderName.c_str()); + MX_TRACE_FUNCTION(mx::Tracing::Category::Render); + MX_TRACE_SCOPE(mx::Tracing::Category::Material, shaderName.c_str()); std::cout << "Validating GLSL rendering for: " << doc->getSourceUri() << std::endl; mx::ScopedTimer totalGLSLTime(&profileTimes.languageTimes.totalTime); @@ -207,7 +206,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, transpTimer.endTimer(); mx::ScopedTimer generationTimer(&profileTimes.languageTimes.generationTime); - MX_TRACE_SCOPE(Cat::ShaderGen, "GenerateShader"); + MX_TRACE_SCOPE(mx::Tracing::Category::ShaderGen, "GenerateShader"); mx::GenOptions& contextOptions = context.getOptions(); contextOptions = options; contextOptions.targetColorSpaceOverride = "lin_rec709"; @@ -284,7 +283,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, _renderer->setLightHandler(isShader ? _lightHandler : nullptr); { - MX_TRACE_SCOPE(Cat::Render, "CompileShader"); + MX_TRACE_SCOPE(mx::Tracing::Category::Render, "CompileShader"); mx::ScopedTimer compileTimer(&profileTimes.languageTimes.compileTime); _renderer->createProgram(shader); _renderer->validateInputs(); @@ -351,7 +350,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, int supersampleFactor = testOptions.enableReferenceQuality ? 8 : 1; { - MX_TRACE_SCOPE(Cat::Render, "RenderMaterial"); + MX_TRACE_SCOPE(mx::Tracing::Category::Render, "RenderMaterial"); mx::ScopedTimer renderTimer(&profileTimes.languageTimes.renderTime); _renderer->getImageHandler()->setSearchPath(imageSearchPath); unsigned int width = (unsigned int) testOptions.renderSize[0] * supersampleFactor; From 742a0111acc2e3432d2f425cecf930ec353ceb02 Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Thu, 12 Feb 2026 16:10:57 -0500 Subject: [PATCH 38/40] Rename MATERIALX_BUILD_TRACING to MATERIALX_BUILD_PERFETTO_TRACING Address review: use a Perfetto-specific flag name to leave a clear path for future extensions to Tracy and other profiling backends. --- CMakeLists.txt | 10 +++++----- source/CMakeLists.txt | 2 +- source/MaterialXTest/CMakeLists.txt | 2 +- .../MaterialXGenShader/GenShaderUtil.cpp | 2 +- .../MaterialXTest/MaterialXGenShader/GenShaderUtil.h | 2 +- source/MaterialXTest/MaterialXRender/RenderUtil.cpp | 4 ++-- source/MaterialXTrace/CMakeLists.txt | 6 +++--- source/MaterialXTrace/PerfettoSink.cpp | 4 ++-- source/MaterialXTrace/PerfettoSink.h | 4 ++-- source/MaterialXTrace/Tracing.h | 12 ++++++------ 10 files changed, 24 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d0fe5b047..632c1f97f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,7 +51,7 @@ option(MATERIALX_BUILD_OCIO "Build OpenColorIO support for shader generators." O option(MATERIALX_BUILD_TESTS "Build unit tests." OFF) option(MATERIALX_BUILD_BENCHMARK_TESTS "Build benchmark tests." OFF) option(MATERIALX_BUILD_OSOS "Build OSL .oso's of standard library shaders for the OSL Network generator" OFF) -option(MATERIALX_BUILD_TRACING "Build with Perfetto tracing support for performance analysis." OFF) +option(MATERIALX_BUILD_PERFETTO_TRACING "Build with Perfetto tracing support for performance analysis." OFF) option(MATERIALX_BUILD_SHARED_LIBS "Build MaterialX libraries as shared rather than static." OFF) option(MATERIALX_BUILD_DATA_LIBRARY "Build generated products from the MaterialX data library." OFF) @@ -229,10 +229,10 @@ mark_as_advanced(MATERIALX_MDL_BINARY_TESTRENDER) mark_as_advanced(MATERIALX_MDL_MODULE_PATHS) mark_as_advanced(MATERIALX_MDL_SDK_DIR) mark_as_advanced(MATERIALX_SLANG_RHI_SOURCE_DIR) -mark_as_advanced(MATERIALX_BUILD_TRACING) +mark_as_advanced(MATERIALX_BUILD_PERFETTO_TRACING) # Perfetto tracing support -if(MATERIALX_BUILD_TRACING) +if(MATERIALX_BUILD_PERFETTO_TRACING) include(FetchContent) FetchContent_Declare( perfetto @@ -243,7 +243,7 @@ if(MATERIALX_BUILD_TRACING) # Only fetch the SDK, not the full Perfetto source set(PERFETTO_SDK_INCLUDE_DIR "${CMAKE_BINARY_DIR}/_deps/perfetto-src/sdk") FetchContent_MakeAvailable(perfetto) - add_definitions(-DMATERIALX_BUILD_TRACING) + add_definitions(-DMATERIALX_BUILD_PERFETTO_TRACING) # Define compile flags for perfetto.cc (Perfetto SDK triggers various warnings) # These variables are used in source/CMakeLists.txt (monolithic) and MaterialXTrace (non-monolithic) @@ -527,7 +527,7 @@ endif() # Add core subdirectories add_subdirectory(source/MaterialXCore) add_subdirectory(source/MaterialXFormat) -if(MATERIALX_BUILD_TRACING) +if(MATERIALX_BUILD_PERFETTO_TRACING) add_subdirectory(source/MaterialXTrace) endif() diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index b0f3a31927..49c19ee8b6 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -7,7 +7,7 @@ if (MATERIALX_BUILD_MONOLITHIC) # Apply Perfetto compile flags for monolithic builds # (set_source_files_properties is directory-scoped, must be called here) - if(MATERIALX_BUILD_TRACING) + if(MATERIALX_BUILD_PERFETTO_TRACING) set_source_files_properties("${perfetto_SOURCE_DIR}/sdk/perfetto.cc" PROPERTIES COMPILE_DEFINITIONS "${MATERIALX_PERFETTO_COMPILE_DEFINITIONS}" diff --git a/source/MaterialXTest/CMakeLists.txt b/source/MaterialXTest/CMakeLists.txt index 1ecc721a6b..9449a9b830 100644 --- a/source/MaterialXTest/CMakeLists.txt +++ b/source/MaterialXTest/CMakeLists.txt @@ -40,7 +40,7 @@ add_subdirectory(MaterialXCore) target_link_libraries(MaterialXTest MaterialXCore) add_subdirectory(MaterialXFormat) target_link_libraries(MaterialXTest MaterialXFormat) -if(MATERIALX_BUILD_TRACING) +if(MATERIALX_BUILD_PERFETTO_TRACING) target_link_libraries(MaterialXTest MaterialXTrace) endif() diff --git a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp index 1212421140..437d701782 100644 --- a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp +++ b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp @@ -669,7 +669,7 @@ void ShaderGeneratorTester::validate(const mx::GenOptions& generateOptions, cons return; } -#ifdef MATERIALX_BUILD_TRACING +#ifdef MATERIALX_BUILD_PERFETTO_TRACING // Set up Perfetto tracing if enabled std::optional tracingGuard; if (options.enableTracing) diff --git a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h index 54b368ab23..e6ae23f19f 100644 --- a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h +++ b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h @@ -122,7 +122,7 @@ class TestSuiteOptions // If empty, use default locations. If set, all artifacts go to this directory. mx::FilePath outputDirectory; - // Enable Perfetto tracing during render tests (requires MATERIALX_BUILD_TRACING). + // Enable Perfetto tracing during render tests (requires MATERIALX_BUILD_PERFETTO_TRACING). // Default is false to avoid overhead when not profiling. bool enableTracing = false; diff --git a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp index f5e2198670..e79387f23a 100644 --- a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp +++ b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp @@ -12,7 +12,7 @@ #include #endif -#ifdef MATERIALX_BUILD_TRACING +#ifdef MATERIALX_BUILD_PERFETTO_TRACING #include #include #endif @@ -99,7 +99,7 @@ bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) return false; } -#ifdef MATERIALX_BUILD_TRACING +#ifdef MATERIALX_BUILD_PERFETTO_TRACING // Initialize tracing with target-specific trace filename (if enabled in options) std::optional tracingGuard; if (options.enableTracing) diff --git a/source/MaterialXTrace/CMakeLists.txt b/source/MaterialXTrace/CMakeLists.txt index d31f3d5a70..bbade0aef2 100644 --- a/source/MaterialXTrace/CMakeLists.txt +++ b/source/MaterialXTrace/CMakeLists.txt @@ -12,7 +12,7 @@ set(materialx_headers ) # Exclude Perfetto sink when tracing is disabled -if(NOT MATERIALX_BUILD_TRACING) +if(NOT MATERIALX_BUILD_PERFETTO_TRACING) list(REMOVE_ITEM materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/Tracing.cpp") list(REMOVE_ITEM materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/PerfettoSink.cpp") # Keep headers but they'll be no-ops via macros @@ -29,7 +29,7 @@ mx_add_library(MaterialXTrace MaterialXCore) # Perfetto tracing support -if(MATERIALX_BUILD_TRACING) +if(MATERIALX_BUILD_PERFETTO_TRACING) # The Perfetto SDK is distributed as an amalgamated single-file source (sdk/perfetto.cc). # This is the official recommended integration method per Google's documentation: # https://perfetto.dev/docs/instrumentation/tracing-sdk @@ -43,7 +43,7 @@ if(MATERIALX_BUILD_TRACING) # Use generator expression - only needed at build time, not install target_include_directories(${TARGET_NAME} PUBLIC $) - target_compile_definitions(${TARGET_NAME} PUBLIC MATERIALX_BUILD_TRACING) + target_compile_definitions(${TARGET_NAME} PUBLIC MATERIALX_BUILD_PERFETTO_TRACING) # Platform-specific link libraries if(WIN32) diff --git a/source/MaterialXTrace/PerfettoSink.cpp b/source/MaterialXTrace/PerfettoSink.cpp index 26b960ebed..691c73c08f 100644 --- a/source/MaterialXTrace/PerfettoSink.cpp +++ b/source/MaterialXTrace/PerfettoSink.cpp @@ -5,7 +5,7 @@ #include -#ifdef MATERIALX_BUILD_TRACING +#ifdef MATERIALX_BUILD_PERFETTO_TRACING #include #include @@ -174,4 +174,4 @@ std::unique_ptr createPerfettoSink(const std::string& outputPath, size_t b MATERIALX_NAMESPACE_END -#endif // MATERIALX_BUILD_TRACING +#endif // MATERIALX_BUILD_PERFETTO_TRACING diff --git a/source/MaterialXTrace/PerfettoSink.h b/source/MaterialXTrace/PerfettoSink.h index c2459c4629..b0012e86ec 100644 --- a/source/MaterialXTrace/PerfettoSink.h +++ b/source/MaterialXTrace/PerfettoSink.h @@ -17,7 +17,7 @@ #include -#ifdef MATERIALX_BUILD_TRACING +#ifdef MATERIALX_BUILD_PERFETTO_TRACING // Suppress verbose warnings from Perfetto SDK templates #ifdef _MSC_VER @@ -81,6 +81,6 @@ class PerfettoSink : public Sink MATERIALX_NAMESPACE_END -#endif // MATERIALX_BUILD_TRACING +#endif // MATERIALX_BUILD_PERFETTO_TRACING #endif // MATERIALX_PERFETTOSINK_H diff --git a/source/MaterialXTrace/Tracing.h b/source/MaterialXTrace/Tracing.h index f76070b00f..283fd33dca 100644 --- a/source/MaterialXTrace/Tracing.h +++ b/source/MaterialXTrace/Tracing.h @@ -184,7 +184,7 @@ class Scope // Sink Factory Functions // ============================================================================ -#ifdef MATERIALX_BUILD_TRACING +#ifdef MATERIALX_BUILD_PERFETTO_TRACING /// Create a Perfetto-based tracing sink. /// @@ -203,7 +203,7 @@ class Scope MX_TRACE_API std::unique_ptr createPerfettoSink( const std::string& outputPath, size_t bufferSizeKb = 32768); -#endif // MATERIALX_BUILD_TRACING +#endif // MATERIALX_BUILD_PERFETTO_TRACING } // namespace Tracing @@ -212,14 +212,14 @@ MATERIALX_NAMESPACE_END // ============================================================================ // Tracing Macros // ============================================================================ -// When MATERIALX_BUILD_TRACING is defined, these macros generate trace events. +// When MATERIALX_BUILD_PERFETTO_TRACING is defined, these macros generate trace events. // Otherwise, they compile to nothing (zero overhead). // Helper macros for token pasting with __LINE__ expansion #define MX_TRACE_CONCAT_IMPL(a, b) a##b #define MX_TRACE_CONCAT(a, b) MX_TRACE_CONCAT_IMPL(a, b) -#ifdef MATERIALX_BUILD_TRACING +#ifdef MATERIALX_BUILD_PERFETTO_TRACING /// Create a scoped trace event. Event ends when scope exits. /// Category must be a Tracing::Category enum value. @@ -242,7 +242,7 @@ MATERIALX_NAMESPACE_END #define MX_TRACE_END(category) \ MaterialX::Tracing::Dispatcher::getInstance().endEvent(category) -#else // MATERIALX_BUILD_TRACING not defined +#else // MATERIALX_BUILD_PERFETTO_TRACING not defined #define MX_TRACE_SCOPE(category, name) #define MX_TRACE_FUNCTION(category) @@ -250,6 +250,6 @@ MATERIALX_NAMESPACE_END #define MX_TRACE_BEGIN(category, name) #define MX_TRACE_END(category) -#endif // MATERIALX_BUILD_TRACING +#endif // MATERIALX_BUILD_PERFETTO_TRACING #endif // MATERIALX_TRACING_H From e2ad8671015b34ed4927e4ffbed5faf7505c9b6d Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Thu, 12 Feb 2026 16:11:21 -0500 Subject: [PATCH 39/40] Enable Perfetto tracing in a single CI build (Linux GCC 14) Address review: instead of enabling tracing in all extended builds, add a matrix.extended_build_perfetto flag and set it on one non-Windows build to produce a single clear tracing artifact. --- .github/workflows/main.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5a377b46c1..cd1ac3a4a5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -31,6 +31,7 @@ jobs: compiler: gcc compiler_version: "14" python: 3.13 + extended_build_perfetto: ON - name: Linux_GCC_14_Python314 os: ubuntu-24.04 @@ -206,8 +207,9 @@ jobs: if [ "${{ matrix.extended_build_mdl_sdk }}" == "ON" -a "${{ runner.os }}" == "Windows" ]; then EXTENDED_BUILD_CONFIG="$EXTENDED_BUILD_CONFIG -DVCPKG_TARGET_TRIPLET=x64-windows-release -DMATERIALX_MDL_SDK_DIR=C:/vcpkg/installed/x64-windows-release" fi - # Enable Perfetto tracing - EXTENDED_BUILD_CONFIG="$EXTENDED_BUILD_CONFIG -DMATERIALX_BUILD_TRACING=ON" + if [ "${{ matrix.extended_build_perfetto }}" == "ON" ]; then + EXTENDED_BUILD_CONFIG="$EXTENDED_BUILD_CONFIG -DMATERIALX_BUILD_PERFETTO_TRACING=ON" + fi fi TEST_RENDER_CONFIG="-DMATERIALX_TEST_RENDER=OFF" if [ "${{ matrix.test_render }}" == "ON" ]; then From e0983491a8a0f66fe20be3610dbddd9fc65721d8 Mon Sep 17 00:00:00 2001 From: Pavlo Penenko Date: Thu, 12 Feb 2026 16:18:38 -0500 Subject: [PATCH 40/40] Gate Perfetto trace upload on matrix.extended_build_perfetto Only attempt to upload trace artifacts from the build that actually has Perfetto enabled, avoiding no-op upload steps in other builds. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cd1ac3a4a5..f9de29a53b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -364,7 +364,7 @@ jobs: - name: Upload Perfetto Traces uses: actions/upload-artifact@v4 - if: env.IS_EXTENDED_BUILD == 'true' + if: matrix.extended_build_perfetto == 'ON' && env.IS_EXTENDED_BUILD == 'true' with: name: Traces_${{ matrix.name }} path: build/**/*.perfetto-trace