diff --git a/CMakeLists.txt b/CMakeLists.txt index c1768dbabc..358a2cf12f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.7) project(vsg - VERSION 1.1.0 + VERSION 1.1.1 DESCRIPTION "VulkanSceneGraph library" LANGUAGES CXX ) @@ -37,6 +37,9 @@ find_package(Vulkan ${Vulkan_MIN_VERSION} REQUIRED) find_package(Threads REQUIRED) +# Set the instrumentation level to compile into sources +set(VSG_MAX_INSTRUMENTATION_LEVEL 1 CACHE STRING "Set the instrumentation level to build into the VSG ibrary, 0 for off, 1 coarse grained, 2 medium, 3 fine grained." ) + # Enable/disable shader compilation support that pulls in glslang set(VSG_SUPPORTS_ShaderCompiler 1 CACHE STRING "Optional shader compiler support, 0 for off, 1 for enabled." ) if (VSG_SUPPORTS_ShaderCompiler) diff --git a/cmake/build_all_h.cmake b/cmake/build_all_h.cmake index 0623a3cc6a..6b86fa0bd9 100644 --- a/cmake/build_all_h.cmake +++ b/cmake/build_all_h.cmake @@ -26,6 +26,9 @@ macro(BUILD_ALL_H) file(GLOB RAYTRACING_HEADERS RELATIVE ${INCLUDE_DIR} ${INCLUDE_DIR}/vsg/raytracing/*.h ) file(GLOB MESHSHADERS_HEADERS RELATIVE ${INCLUDE_DIR} ${INCLUDE_DIR}/vsg/meshshaders/*.h ) + # remove items that are inapprorpiate to include in all.h as they are optional have external dependencies not provided by the VSG itself. + list (REMOVE_ITEM UTILS_HEADERS "vsg/utils/TracyInstrumentation.h") + file(READ ${VSG_SOURCE_DIR}/cmake/header_license_preamble.txt ALL_H_CONTENTS) APPEND_INCLUDES(ALL_H_CONTENTS CORE_HEADERS "// Core header files\n") APPEND_INCLUDES(ALL_H_CONTENTS MATHS_HEADERS "// Maths header files\n") diff --git a/include/vsg/all.h b/include/vsg/all.h index da7c22e2b7..585b2f7ffd 100644 --- a/include/vsg/all.h +++ b/include/vsg/all.h @@ -64,6 +64,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include #include #include #include @@ -257,7 +258,9 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include #include +#include #include #include #include diff --git a/include/vsg/app/CommandGraph.h b/include/vsg/app/CommandGraph.h index f189b82e6d..1dfb2a9171 100644 --- a/include/vsg/app/CommandGraph.h +++ b/include/vsg/app/CommandGraph.h @@ -17,6 +17,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include #include namespace vsg @@ -41,11 +42,7 @@ namespace vsg uint32_t maxSlot = 2; int submitOrder = 0; - inline ref_ptr getOrCreateRecordTraversal() - { - if (!recordTraversal) recordTraversal = RecordTraversal::create(maxSlot); - return recordTraversal; - } + ref_ptr getOrCreateRecordTraversal(); ref_ptr recordTraversal; @@ -53,6 +50,9 @@ namespace vsg virtual void reset(); virtual void record(ref_ptr recordedCommandBuffers, ref_ptr frameStamp = {}, ref_ptr databasePager = {}); + /// hook for assigning Instrumentation to enable profiling of record traversal. + ref_ptr instrumentation; + protected: virtual ~CommandGraph(); diff --git a/include/vsg/app/CompileManager.h b/include/vsg/app/CompileManager.h index 13b3850030..1d6a092710 100644 --- a/include/vsg/app/CompileManager.h +++ b/include/vsg/app/CompileManager.h @@ -58,6 +58,9 @@ namespace vsg /// add a compile Context for all the Views assigned to a Viewer void add(const Viewer& viewer, const ResourceRequirements& resourceRequirements = {}); + /// assign Instrumentation to all CompileTraversal and their associated Context + void assignInstrumentation(ref_ptr in_instrumentation); + using ContextSelectionFunction = std::function; /// compile object diff --git a/include/vsg/app/CompileTraversal.h b/include/vsg/app/CompileTraversal.h index f8aa26ee67..2dd3f24758 100644 --- a/include/vsg/app/CompileTraversal.h +++ b/include/vsg/app/CompileTraversal.h @@ -19,6 +19,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include #include #include #include @@ -45,6 +46,9 @@ namespace vsg /// list of Context that Vulkan objects should be compiled for. std::list> contexts; + /// Hook for assigning Instrumentation to enable profiling + ref_ptr instrumentation; + /// add a compile Context for device void add(ref_ptr device, const ResourceRequirements& resourceRequirements = {}); @@ -60,6 +64,11 @@ namespace vsg /// add a compile Context for all the Views assigned to a Viewer void add(const Viewer& viewer, const ResourceRequirements& resourceRequirements = {}); + /// assign Instrumentation to all Context + void assignInstrumentation(ref_ptr in_instrumentation); + + Instrumentation* getInstrumentation() override { return instrumentation.get(); } + virtual bool record(); virtual void waitForCompletion(); diff --git a/include/vsg/app/RecordAndSubmitTask.h b/include/vsg/app/RecordAndSubmitTask.h index c8b2c5a4b4..aed0570534 100644 --- a/include/vsg/app/RecordAndSubmitTask.h +++ b/include/vsg/app/RecordAndSubmitTask.h @@ -17,6 +17,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include #include namespace vsg @@ -59,6 +60,12 @@ namespace vsg ref_ptr databasePager; + /// hook for assigning Instrumentation to enable profiling of record traversal. + ref_ptr instrumentation; + + /// Convenience method for assigning Instrumentation to the viewer and any associated objects. + void assignInstrumentation(ref_ptr in_instrumentation); + protected: size_t _currentFrameIndex; std::vector _indices; diff --git a/include/vsg/app/RecordTraversal.h b/include/vsg/app/RecordTraversal.h index a120e7be97..51a78ad3df 100644 --- a/include/vsg/app/RecordTraversal.h +++ b/include/vsg/app/RecordTraversal.h @@ -35,6 +35,10 @@ namespace vsg class DepthSorted; class Transform; class MatrixTransform; + class TileDatabase; + class VertexDraw; + class VertexIndexDraw; + class Geometry; class Command; class Commands; class CommandBuffer; @@ -53,6 +57,7 @@ namespace vsg class SpotLight; class CommandGraph; class RecordedCommandBuffers; + class Instrumentation; VSG_type_name(vsg::RecordTraversal); @@ -77,6 +82,8 @@ namespace vsg Mask traversalMask = MASK_ALL; Mask overrideMask = MASK_OFF; + ref_ptr instrumentation; + /// Container for CommandBuffers that have been recorded in current frame ref_ptr recordedCommandBuffers; @@ -102,11 +109,17 @@ namespace vsg void apply(const QuadGroup& quadGroup); void apply(const LOD& lod); void apply(const PagedLOD& pagedLOD); + void apply(const TileDatabase& tileDatabase); void apply(const CullGroup& cullGroup); void apply(const CullNode& cullNode); void apply(const DepthSorted& depthSorted); void apply(const Switch& sw); + // leaf node + void apply(const VertexDraw& vid); + void apply(const VertexIndexDraw& vid); + void apply(const Geometry& vid); + // positional state void apply(const Light& light); void apply(const AmbientLight& light); @@ -118,16 +131,20 @@ namespace vsg void apply(const Transform& transform); void apply(const MatrixTransform& mt); void apply(const StateGroup& object); + + // Commands void apply(const Commands& commands); void apply(const Command& command); // Viewer level nodes + void apply(const Bin& bin); void apply(const View& view); void apply(const CommandGraph& commandGraph); // clear the bins to record a new frame. void clearBins(); + protected: virtual ~RecordTraversal(); diff --git a/include/vsg/app/TransferTask.h b/include/vsg/app/TransferTask.h index 7ff1637b4c..b41f26df35 100644 --- a/include/vsg/app/TransferTask.h +++ b/include/vsg/app/TransferTask.h @@ -49,6 +49,9 @@ namespace vsg ref_ptr transferQueue; ref_ptr currentTransferCompletedSemaphore; + /// hook for assigning Instrumentation to enable profiling of record traversal. + ref_ptr instrumentation; + protected: using OffsetBufferInfoMap = std::map>; using BufferMap = std::map, OffsetBufferInfoMap>; diff --git a/include/vsg/app/Viewer.h b/include/vsg/app/Viewer.h index 513b6f9112..6e2b4462ff 100644 --- a/include/vsg/app/Viewer.h +++ b/include/vsg/app/Viewer.h @@ -19,6 +19,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include #include @@ -133,6 +134,12 @@ namespace vsg /// Call vkDeviceWaitIdle on all the devices associated with this Viewer virtual void deviceWaitIdle() const; + /// Hook for assigning Instrumentation to enable profiling of record traversal. + ref_ptr instrumentation; + + /// Convenience method for assigning Instrumentation to the viewer and any associated objects. + void assignInstrumentation(ref_ptr in_instrumentation); + protected: virtual ~Viewer(); diff --git a/include/vsg/core/ConstVisitor.h b/include/vsg/core/ConstVisitor.h index 17696c3fef..379ba0dcae 100644 --- a/include/vsg/core/ConstVisitor.h +++ b/include/vsg/core/ConstVisitor.h @@ -47,6 +47,7 @@ namespace vsg class DirectionalLight; class PointLight; class SpotLight; + class InstrumentationNode; // forward declare text classes class Text; @@ -142,6 +143,7 @@ namespace vsg // forward declare general classes class FrameStamp; + class Instrumentation; class VSG_DECLSPEC ConstVisitor : public Object { @@ -151,6 +153,8 @@ namespace vsg Mask traversalMask = MASK_ALL; Mask overrideMask = MASK_OFF; + virtual Instrumentation* getInstrumentation() { return nullptr; } + virtual void apply(const Object&); virtual void apply(const Objects&); virtual void apply(const External&); @@ -302,6 +306,7 @@ namespace vsg virtual void apply(const DirectionalLight&); virtual void apply(const PointLight&); virtual void apply(const SpotLight&); + virtual void apply(const InstrumentationNode&); // text virtual void apply(const Text&); diff --git a/include/vsg/core/Visitor.h b/include/vsg/core/Visitor.h index 4a0d0909a5..9c67e2a978 100644 --- a/include/vsg/core/Visitor.h +++ b/include/vsg/core/Visitor.h @@ -47,6 +47,7 @@ namespace vsg class DirectionalLight; class PointLight; class SpotLight; + class InstrumentationNode; // forward declare text classes class Text; @@ -142,6 +143,7 @@ namespace vsg // forward declare general classes class FrameStamp; + class Instrumentation; class VSG_DECLSPEC Visitor : public Object { @@ -151,6 +153,8 @@ namespace vsg Mask traversalMask = MASK_ALL; Mask overrideMask = MASK_OFF; + virtual Instrumentation* getInstrumentation() { return nullptr; } + virtual void apply(Object&); virtual void apply(Objects&); virtual void apply(External&); @@ -302,6 +306,7 @@ namespace vsg virtual void apply(DirectionalLight&); virtual void apply(PointLight&); virtual void apply(SpotLight&); + virtual void apply(InstrumentationNode&); // text virtual void apply(Text&); diff --git a/include/vsg/io/DatabasePager.h b/include/vsg/io/DatabasePager.h index fb669843f5..36407dc983 100644 --- a/include/vsg/io/DatabasePager.h +++ b/include/vsg/io/DatabasePager.h @@ -12,16 +12,14 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI */ +#include #include #include #include #include - #include - #include - -#include +#include #include #include @@ -96,8 +94,6 @@ namespace vsg virtual void updateSceneGraph(FrameStamp* frameStamp, CompileResult& cr); - ref_ptr options; - ref_ptr compileManager; std::atomic_uint numActiveRequests{0}; @@ -112,6 +108,12 @@ namespace vsg ref_ptr pagedLODContainer; + /// Hook for assigning Instrumentation to enable profiling + ref_ptr instrumentation; + + /// assign Instrumentation to all CompileTraversal and their associated Context + void assignInstrumentation(ref_ptr in_instrumentation); + protected: virtual ~DatabasePager(); diff --git a/include/vsg/io/Options.h b/include/vsg/io/Options.h index 27e351d027..caa3982d29 100644 --- a/include/vsg/io/Options.h +++ b/include/vsg/io/Options.h @@ -17,6 +17,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include namespace vsg { @@ -96,6 +97,9 @@ namespace vsg /// scene graph creation routines can use the inherited state information to avoid setting state in the local subgraph. StateCommands inheritedState; + /// Hook for assigning Instrumentation to enable profiling of record traversal. + ref_ptr instrumentation; + protected: virtual ~Options(); }; diff --git a/include/vsg/maths/color.h b/include/vsg/maths/color.h index 7d18d8fd6e..7db673fd5c 100644 --- a/include/vsg/maths/color.h +++ b/include/vsg/maths/color.h @@ -99,28 +99,28 @@ namespace vsg template constexpr t_vec4 linear_to_sRGB(const t_vec4& src) { - const T exponent = 2.2; + const T exponent = static_cast(2.2); return t_vec4(std::pow(src.r, exponent), std::pow(src.g, exponent), std::pow(src.b, exponent), src.a); } template constexpr t_vec4 linear_to_sRGB(T r, T g, T b, T a) { - const T exponent = 2.2; + const T exponent = static_cast(2.2); return t_vec4(std::pow(r, exponent), std::pow(g, exponent), std::pow(b, exponent), a); } template constexpr t_vec4 sRGB_to_linear(const t_vec4& src) { - const T exponent = 1.0 / 2.2; + const T exponent = static_cast(1.0 / 2.2); return t_vec4(std::pow(src.r, exponent), std::pow(src.g, exponent), std::pow(src.b, exponent), src.a); } template constexpr t_vec4 sRGB_to_linear(T r, T g, T b, T a) { - const T exponent = 1.0 / 2.2; + const T exponent = static_cast(1.0 / 2.2); return t_vec4(std::pow(r, exponent), std::pow(g, exponent), std::pow(b, exponent), a); } diff --git a/include/vsg/maths/vec4.h b/include/vsg/maths/vec4.h index 7954f6ec15..35db054e0f 100644 --- a/include/vsg/maths/vec4.h +++ b/include/vsg/maths/vec4.h @@ -62,8 +62,8 @@ namespace vsg constexpr t_vec4(value_type in_x, value_type in_y, value_type in_z, value_type in_w) : value{in_x, in_y, in_z, in_w} {} - constexpr t_vec4(const VkClearColorValue& v) : - value{static_cast(v.float32[0]), static_cast(v.float32[1]), static_cast(v.float32[2]), static_cast(v.float32[3])} {} + constexpr explicit t_vec4(const VkClearColorValue& v) : + value{ static_cast(v.float32[0]), static_cast(v.float32[1]), static_cast(v.float32[2]), static_cast(v.float32[3])} {} template constexpr explicit t_vec4(const t_vec4& v) : diff --git a/include/vsg/nodes/InstrumentationNode.h b/include/vsg/nodes/InstrumentationNode.h new file mode 100644 index 0000000000..8fe34342b9 --- /dev/null +++ b/include/vsg/nodes/InstrumentationNode.h @@ -0,0 +1,61 @@ +#pragma once + +/* + +Copyright(c) 2024 Robert Osfield + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + */ + +#include +#include +#include + +namespace vsg +{ + + /// InstrumentationNode enables instrumentation of a subgraph + class VSG_DECLSPEC InstrumentationNode : public Inherit + { + public: + InstrumentationNode(); + InstrumentationNode(ref_ptr in_child); + + void traverse(Visitor& visitor) override; + void traverse(ConstVisitor& visitor) const override; + void traverse(RecordTraversal& visitor) const override; + + void read(Input& input) override; + void write(Output& output) const override; + + void setColor(uint_color color); + uint_color getColor() const { return _color; } + + void setName(const std::string& name); + const std::string& getName() const { return _name; } + + void setLevel(uint32_t level); + uint32_t getLevel() const { return _level; } + + ref_ptr child; + + protected: + virtual ~InstrumentationNode(); + + uint32_t _level = 1; + uint_color _color; + std::string _name; + + // SourceLocation variants for passing to Instrumentation that adapt the level, color and name to work with SourceLocation usad by Instrumentation + SourceLocation _sl_Visitor; + SourceLocation _sl_ConstVisitor; + SourceLocation _sl_RecordTraversal; + }; + VSG_type_name(vsg::InstrumentationNode); + +} // namespace vsg diff --git a/include/vsg/utils/GpuAnnotation.h b/include/vsg/utils/GpuAnnotation.h new file mode 100644 index 0000000000..8c4d08c486 --- /dev/null +++ b/include/vsg/utils/GpuAnnotation.h @@ -0,0 +1,47 @@ +#pragma once + +/* + +Copyright(c) 2023 Robert Osfield + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + */ + +#include + +namespace vsg +{ + + /// GpuAnnotationo is a vsg::Instrumentation subclasses that uses VK_debug_utils to emit annotation of the scene graph traversal. + /// Provides tools like RenderDoc a way to report the source location associated with Vulkan calls. + class VSG_DECLSPEC GpuAnnotation : public Inherit + { + public: + GpuAnnotation(); + + enum LabelType + { + SourceLocation_name, + SourceLocation_function, + Object_className, + }; + + LabelType labelType = SourceLocation_name; + + void enterCommandBuffer(const SourceLocation* sl, uint64_t& reference, CommandBuffer& commandBuffer) const override; + void leaveCommandBuffer(const SourceLocation* sl, uint64_t& reference, CommandBuffer& commandBuffer) const override; + + void enter(const vsg::SourceLocation* sl, uint64_t& reference, CommandBuffer& commandBuffer, const Object* object) const override; + void leave(const vsg::SourceLocation* sl, uint64_t& reference, CommandBuffer& commandBuffer, const Object* object) const override; + + protected: + virtual ~GpuAnnotation(); + }; + VSG_type_name(vsg::GpuAnnotation); + +} // namespace vsg diff --git a/include/vsg/utils/Instrumentation.h b/include/vsg/utils/Instrumentation.h new file mode 100644 index 0000000000..6ee2bc4ffd --- /dev/null +++ b/include/vsg/utils/Instrumentation.h @@ -0,0 +1,307 @@ +#pragma once + +/* + +Copyright(c) 2023 Robert Osfield + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + */ + +#include +#include +#include +#include + +namespace vsg +{ + + // forward declare + class CommandBuffer; + class FrameStamp; + + /// uint_color struct used to provide a {r, g, b, a} interface a colors assigned as uint32_t + struct uint_color + { + // BGRA order required to map to Tracy's uint32_t color value + uint8_t b = 255, g = 255, r = 255, a = 255; + constexpr uint_color() = default; + constexpr uint_color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) : + b(blue), g(green), r(red), a(alpha) {} + }; + + /// SourceLocation structs mark the location in a source file when instrumentation is placed. + /// Memory layout was chosen to be compatible to Tracy's SourceLocationData object. + struct SourceLocation + { + const char* name; + const char* function; + const char* file; + uint32_t line; + uint_color color; + uint32_t level; + }; + + /// base class for Instrumentation implentations + class VSG_DECLSPEC Instrumentation : public Inherit + { + public: + Instrumentation(); + + virtual ref_ptr shareOrDuplicateForThreadSafety() { return ref_ptr(this); } + + virtual void setThreadName(const std::string& /*name*/) const {}; + + virtual void enterFrame(const SourceLocation* /*sl*/, uint64_t& /*reference*/, FrameStamp& /*frameStamp*/) const {}; + virtual void leaveFrame(const SourceLocation* /*sl*/, uint64_t& /*reference*/, FrameStamp& /*frameStamp*/) const {}; + + virtual void enter(const SourceLocation* /*sl*/, uint64_t& /*reference*/, const Object* /*object*/ = nullptr) const {}; + virtual void leave(const SourceLocation* /*sl*/, uint64_t& /*reference*/, const Object* /*object*/ = nullptr) const {}; + + virtual void enterCommandBuffer(const SourceLocation* /*sl*/, uint64_t& /*reference*/, CommandBuffer& /*commandBuffer*/) const {}; + virtual void leaveCommandBuffer(const SourceLocation* /*sl*/, uint64_t& /*reference*/, CommandBuffer& /*commandBuffer*/) const {}; + + virtual void enter(const SourceLocation* /*sl*/, uint64_t& /*reference*/, CommandBuffer& /*commandBuffer*/, const Object* /*object*/ = nullptr) const {}; + virtual void leave(const SourceLocation* /*sl*/, uint64_t& /*reference*/, CommandBuffer& /*commandBuffer*/, const Object* /*object*/ = nullptr) const {}; + + protected: + virtual ~Instrumentation(); + }; + VSG_type_name(vsg::Instrumentation); + + /// convinience static method for sharing or duplicating Instrumentation if a valid Instrumentation object is provided + extern ref_ptr shareOrDuplicateForThreadSafety(ref_ptr instrumentation); + + struct CpuInstrumentation + { + const Instrumentation* instr; + const SourceLocation* sl; + uint64_t reference; + const Object* object; + + inline CpuInstrumentation(const Instrumentation* in_instr, const SourceLocation* in_sl, const Object* in_object = nullptr) : + instr(in_instr), sl(in_sl), object(in_object) + { + if (instr) instr->enter(sl, reference, object); + } + inline ~CpuInstrumentation() + { + if (instr) instr->leave(sl, reference, object); + } + }; + + struct GpuInstrumentation + { + const Instrumentation* instr; + const SourceLocation* sl; + uint64_t reference; + CommandBuffer& commandBuffer; + const Object* object; + + inline GpuInstrumentation(const Instrumentation* in_instr, const SourceLocation* in_sl, CommandBuffer& in_commandBuffer, const Object* in_object = nullptr) : + instr(in_instr), sl(in_sl), commandBuffer(in_commandBuffer), object(in_object) + { + if (instr) instr->enter(sl, reference, commandBuffer, object); + } + inline ~GpuInstrumentation() + { + if (instr) instr->leave(sl, reference, commandBuffer, object); + } + }; + + struct CommandBufferInstrumentation + { + const Instrumentation* instr; + const SourceLocation* sl; + uint64_t reference; + CommandBuffer& commandBuffer; + + inline CommandBufferInstrumentation(const Instrumentation* in_instr, const SourceLocation* in_sl, CommandBuffer& in_commandBuffer) : + instr(in_instr), sl(in_sl), commandBuffer(in_commandBuffer) + { + if (instr) instr->enterCommandBuffer(sl, reference, commandBuffer); + } + inline ~CommandBufferInstrumentation() + { + if (instr) instr->leaveCommandBuffer(sl, reference, commandBuffer); + } + }; + +// standard colours specified in {r, g, b, a} ordering +#define COLOR_VIEWER uint_color(127, 240, 240, 255) +#define COLOR_UPDATE uint_color(0, 255, 0, 255) +#define COLOR_GPU uint_color(255, 127, 0, 255) +#define COLOR_RECORD_L1 uint_color(140, 247, 0, 255) +#define COLOR_RECORD_L2 uint_color(176, 176, 0, 255) +#define COLOR_RECORD_L3 COLOR_GPU +#define COLOR_RECORD COLOR_RECORD_L1 +#define COLOR_COMPILE uint_color(255, 249, 64, 255) +#define COLOR_PAGER uint_color(240, 255, 64, 255) +#define COLOR_READ uint_color(0, 255, 128, 255) +#define COLOR_WRITE uint_color(0, 128, 255, 255) + +#if defined(__clang__) || defined(__GNUC__) +# define VsgFunctionName __PRETTY_FUNCTION__ +#elif defined(_MSC_VER) +# define VsgFunctionName __FUNCSIG__ +#endif + +#define __CPU_INSTRUMENTATION(level, instrumentation, name, color, object) \ + static constexpr SourceLocation s_cpu_source_location_##__LINE__{name, VsgFunctionName, __FILE__, __LINE__, color, level}; \ + CpuInstrumentation __cpu_scoped_instrumentation_##__LINE__(instrumentation, &(s_cpu_source_location_##__LINE__), object); + +#define __GPU_INSTRUMENTATION(level, instrumentation, cg, name, color, object) \ + static constexpr SourceLocation s_gpu_source_location_##__LINE__{name, VsgFunctionName, __FILE__, __LINE__, color, level}; \ + GpuInstrumentation __gpu_scoped_instrumentation_##__LINE__(instrumentation, &(s_gpu_source_location_##__LINE__), cg, object); + +#define __COMMAND_BUFFER_INSTRUMENTATION(level, instrumentation, cg, name, color) \ + static constexpr SourceLocation s_cg_source_location_##__LINE__{name, VsgFunctionName, __FILE__, __LINE__, color, level}; \ + CommandBufferInstrumentation __cg_scoped_instrumentation_##__LINE__(instrumentation, &(s_cg_source_location_##__LINE__), cg); + +#if VSG_MAX_INSTRUMENTATION_LEVEL >= 1 + +# define CPU_INSTRUMENTATION_L1(instrumentation) __CPU_INSTRUMENTATION(1, instrumentation, nullptr, uint_color(255, 255, 255, 255), nullptr) +# define CPU_INSTRUMENTATION_L1_N(instrumentation, name) __CPU_INSTRUMENTATION(1, instrumentation, name, uint_color(255, 255, 255, 255), nullptr) +# define CPU_INSTRUMENTATION_L1_C(instrumentation, color) __CPU_INSTRUMENTATION(1, instrumentation, nullptr, color, nullptr) +# define CPU_INSTRUMENTATION_L1_NC(instrumentation, name, color) __CPU_INSTRUMENTATION(1, instrumentation, name, color, nullptr) + +# define GPU_INSTRUMENTATION_L1(instrumentation, cg) __GPU_INSTRUMENTATION(1, instrumentation, cg, nullptr, uint_color(255, 255, 255, 255), nullptr) +# define GPU_INSTRUMENTATION_L1_N(instrumentation, cg, name) __GPU_INSTRUMENTATION(1, instrumentation, cg, name, uint_color(255, 255, 255, 255), nullptr) +# define GPU_INSTRUMENTATION_L1_C(instrumentation, cg, color) __GPU_INSTRUMENTATION(1, instrumentation, cg, nullptr, color, nullptr) +# define GPU_INSTRUMENTATION_L1_NC(instrumentation, cg, name, color) __GPU_INSTRUMENTATION(1, instrumentation, cg, name, color, nullptr) + +# define CPU_INSTRUMENTATION_L1_O(instrumentation, object) __CPU_INSTRUMENTATION(1, instrumentation, nullptr, uint_color(255, 255, 255, 255), object) +# define CPU_INSTRUMENTATION_L1_NO(instrumentation, name, object) __CPU_INSTRUMENTATION(1, instrumentation, name, uint_color(255, 255, 255, 255), object) +# define CPU_INSTRUMENTATION_L1_CO(instrumentation, color, object) __CPU_INSTRUMENTATION(1, instrumentation, nullptr, color, object) +# define CPU_INSTRUMENTATION_L1_NCO(instrumentation, name, color, object) __CPU_INSTRUMENTATION(1, instrumentation, name, color, object) + +# define GPU_INSTRUMENTATION_L1_O(instrumentation, cg, object) __GPU_INSTRUMENTATION(1, instrumentation, cg, nullptr, uint_color(255, 255, 255, 255), object) +# define GPU_INSTRUMENTATION_L1_NO(instrumentation, cg, name, object) __GPU_INSTRUMENTATION(1, instrumentation, cg, name, uint_color(255, 255, 255, 255), object) +# define GPU_INSTRUMENTATION_L1_CO(instrumentation, cg, color, object) __GPU_INSTRUMENTATION(1, instrumentation, cg, nullptr, color, object) +# define GPU_INSTRUMENTATION_L1_NCO(instrumentation, cg, name, color, object) __GPU_INSTRUMENTATION(1, instrumentation, cg, name, color, object) + +# define COMMAND_BUFFER_INSTRUMENTATION(instrumentation, cg, name, color) __COMMAND_BUFFER_INSTRUMENTATION(1, instrumentation, cg, name, color) + +#else + +# define CPU_INSTRUMENTATION_L1(instrumentation) +# define CPU_INSTRUMENTATION_L1_N(instrumentation, name) +# define CPU_INSTRUMENTATION_L1_C(instrumentation, color) +# define CPU_INSTRUMENTATION_L1_NC(instrumentation, name, color) + +# define GPU_INSTRUMENTATION_L1(instrumentation, cg) +# define GPU_INSTRUMENTATION_L1_N(instrumentation, cg, name) +# define GPU_INSTRUMENTATION_L1_C(instrumentation, cg, color) +# define GPU_INSTRUMENTATION_L1_NC(instrumentation, cg, name, color) + +# define CPU_INSTRUMENTATION_L1_O(instrumentation, object) +# define CPU_INSTRUMENTATION_L1_NO(instrumentation, name, object) +# define CPU_INSTRUMENTATION_L1_CO(instrumentation, color, object) +# define CPU_INSTRUMENTATION_L1_NCO(instrumentation, name, color, object) + +# define GPU_INSTRUMENTATION_L1_O(instrumentation, cg, object) +# define GPU_INSTRUMENTATION_L1_NO(instrumentation, cg, name, object) +# define GPU_INSTRUMENTATION_L1_CO(instrumentation, cg, color, object) +# define GPU_INSTRUMENTATION_L1_NCO(instrumentation, cg, name, color, object) + +# define COMMAND_BUFFER_INSTRUMENTATION(instrumentation, cg, name, color) + +#endif + +#if VSG_MAX_INSTRUMENTATION_LEVEL >= 2 + +# define CPU_INSTRUMENTATION_L2(instrumentation) __CPU_INSTRUMENTATION(2, instrumentation, nullptr, uint_color(255, 255, 255, 255), nullptr) +# define CPU_INSTRUMENTATION_L2_N(instrumentation, name) __CPU_INSTRUMENTATION(2, instrumentation, name, uint_color(255, 255, 255, 255), nullptr) +# define CPU_INSTRUMENTATION_L2_C(instrumentation, color) __CPU_INSTRUMENTATION(2, instrumentation, nullptr, color, nullptr) +# define CPU_INSTRUMENTATION_L2_NC(instrumentation, name, color) __CPU_INSTRUMENTATION(2, instrumentation, name, color, nullptr) + +# define GPU_INSTRUMENTATION_L2(instrumentation, cg) __GPU_INSTRUMENTATION(2, instrumentation, cg, nullptr, uint_color(255, 255, 255, 255), nullptr) +# define GPU_INSTRUMENTATION_L2_N(instrumentation, cg, name) __GPU_INSTRUMENTATION(2, instrumentation, cg, name, uint_color(255, 255, 255, 255), nullptr) +# define GPU_INSTRUMENTATION_L2_C(instrumentation, cg, color) __GPU_INSTRUMENTATION(2, instrumentation, cg, nullptr, color, nullptr) +# define GPU_INSTRUMENTATION_L2_NC(instrumentation, cg, name, color) __GPU_INSTRUMENTATION(2, instrumentation, cg, name, color, nullptr) + +# define CPU_INSTRUMENTATION_L2_O(instrumentation, object) __CPU_INSTRUMENTATION(2, instrumentation, nullptr, uint_color(255, 255, 255, 255), object) +# define CPU_INSTRUMENTATION_L2_NO(instrumentation, name, object) __CPU_INSTRUMENTATION(2, instrumentation, name, uint_color(255, 255, 255, 255), object) +# define CPU_INSTRUMENTATION_L2_CO(instrumentation, color, object) __CPU_INSTRUMENTATION(2, instrumentation, nullptr, color, object) +# define CPU_INSTRUMENTATION_L2_NCO(instrumentation, name, color, object) __CPU_INSTRUMENTATION(2, instrumentation, name, color, object) + +# define GPU_INSTRUMENTATION_L2_O(instrumentation, cg, object) __GPU_INSTRUMENTATION(2, instrumentation, cg, nullptr, uint_color(255, 255, 255, 255), object) +# define GPU_INSTRUMENTATION_L2_NO(instrumentation, cg, name, object) __GPU_INSTRUMENTATION(2, instrumentation, cg, name, uint_color(255, 255, 255, 255), object) +# define GPU_INSTRUMENTATION_L2_CO(instrumentation, cg, color, object) __GPU_INSTRUMENTATION(2, instrumentation, cg, nullptr, color, object) +# define GPU_INSTRUMENTATION_L2_NCO(instrumentation, cg, name, color, object) __GPU_INSTRUMENTATION(2, instrumentation, cg, name, color, object) + +#else + +# define CPU_INSTRUMENTATION_L2(instrumentation) +# define CPU_INSTRUMENTATION_L2_N(instrumentation, name) +# define CPU_INSTRUMENTATION_L2_C(instrumentation, color) +# define CPU_INSTRUMENTATION_L2_NC(instrumentation, name, color) + +# define GPU_INSTRUMENTATION_L2(instrumentation, cg) +# define GPU_INSTRUMENTATION_L2_N(instrumentation, cg, name) +# define GPU_INSTRUMENTATION_L2_C(instrumentation, cg, color) +# define GPU_INSTRUMENTATION_L2_NC(instrumentation, cg, name, color) + +# define CPU_INSTRUMENTATION_L2_O(instrumentation, object) +# define CPU_INSTRUMENTATION_L2_NO(instrumentation, name, object) +# define CPU_INSTRUMENTATION_L2_CO(instrumentation, color, object) +# define CPU_INSTRUMENTATION_L2_NCO(instrumentation, name, color, object) + +# define GPU_INSTRUMENTATION_L2_O(instrumentation, cg, object) +# define GPU_INSTRUMENTATION_L2_NO(instrumentation, cg, name, object) +# define GPU_INSTRUMENTATION_L2_CO(instrumentation, cg, color, object) +# define GPU_INSTRUMENTATION_L2_NCO(instrumentation, cg, name, color, object) + +#endif + +#if VSG_MAX_INSTRUMENTATION_LEVEL >= 3 + +# define CPU_INSTRUMENTATION_L3(instrumentation) __CPU_INSTRUMENTATION(3, instrumentation, nullptr, uint_color(255, 255, 255, 255), nullptr) +# define CPU_INSTRUMENTATION_L3_N(instrumentation, name) __CPU_INSTRUMENTATION(3, instrumentation, name, uint_color(255, 255, 255, 255), nullptr) +# define CPU_INSTRUMENTATION_L3_C(instrumentation, color) __CPU_INSTRUMENTATION(3, instrumentation, nullptr, color, nullptr) +# define CPU_INSTRUMENTATION_L3_NC(instrumentation, name, color) __CPU_INSTRUMENTATION(3, instrumentation, name, color, nullptr) + +# define GPU_INSTRUMENTATION_L3(instrumentation, cg) __GPU_INSTRUMENTATION(3, instrumentation, cg, nullptr, uint_color(255, 255, 255, 255), nullptr) +# define GPU_INSTRUMENTATION_L3_N(instrumentation, cg, name) __GPU_INSTRUMENTATION(3, instrumentation, cg, name, uint_color(255, 255, 255, 255), nullptr) +# define GPU_INSTRUMENTATION_L3_C(instrumentation, cg, color) __GPU_INSTRUMENTATION(3, instrumentation, cg, nullptr, color, nullptr) +# define GPU_INSTRUMENTATION_L3_NC(instrumentation, cg, name, color) __GPU_INSTRUMENTATION(3, instrumentation, cg, name, color, nullptr) + +# define CPU_INSTRUMENTATION_L3_O(instrumentation, object) __CPU_INSTRUMENTATION(3, instrumentation, nullptr, uint_color(255, 255, 255, 255), object) +# define CPU_INSTRUMENTATION_L3_NO(instrumentation, name, object) __CPU_INSTRUMENTATION(3, instrumentation, name, uint_color(255, 255, 255, 255), object) +# define CPU_INSTRUMENTATION_L3_CO(instrumentation, color, object) __CPU_INSTRUMENTATION(3, instrumentation, nullptr, color, object) +# define CPU_INSTRUMENTATION_L3_NCO(instrumentation, name, color, object) __CPU_INSTRUMENTATION(3, instrumentation, name, color, object) + +# define GPU_INSTRUMENTATION_L3_O(instrumentation, cg, object) __GPU_INSTRUMENTATION(3, instrumentation, cg, nullptr, uint_color(255, 255, 255, 255), object) +# define GPU_INSTRUMENTATION_L3_NO(instrumentation, cg, name, object) __GPU_INSTRUMENTATION(3, instrumentation, cg, name, uint_color(255, 255, 255, 255), object) +# define GPU_INSTRUMENTATION_L3_CO(instrumentation, cg, color, object) __GPU_INSTRUMENTATION(3, instrumentation, cg, nullptr, color, object) +# define GPU_INSTRUMENTATION_L3_NCO(instrumentation, cg, name, color, object) __GPU_INSTRUMENTATION(3, instrumentation, cg, name, color, object) + +#else + +# define CPU_INSTRUMENTATION_L3(instrumentation) +# define CPU_INSTRUMENTATION_L3_N(instrumentation, name) +# define CPU_INSTRUMENTATION_L3_C(instrumentation, color) +# define CPU_INSTRUMENTATION_L3_NC(instrumentation, name, color) + +# define GPU_INSTRUMENTATION_L3(instrumentation, cg) +# define GPU_INSTRUMENTATION_L3_N(instrumentation, cg, name) +# define GPU_INSTRUMENTATION_L3_C(instrumentation, cg, color) +# define GPU_INSTRUMENTATION_L3_NC(instrumentation, cg, name, color) + +# define CPU_INSTRUMENTATION_L3_O(instrumentation, object) +# define CPU_INSTRUMENTATION_L3_NO(instrumentation, name, object) +# define CPU_INSTRUMENTATION_L3_CO(instrumentation, color, object) +# define CPU_INSTRUMENTATION_L3_NCO(instrumentation, name, color, object) + +# define GPU_INSTRUMENTATION_L3_O(instrumentation, cg, object) +# define GPU_INSTRUMENTATION_L3_NO(instrumentation, cg, name, object) +# define GPU_INSTRUMENTATION_L3_CO(instrumentation, cg, color, object) +# define GPU_INSTRUMENTATION_L3_NCO(instrumentation, cg, name, color, object) + +#endif + +} // namespace vsg diff --git a/include/vsg/utils/TracyInstrumentation.h b/include/vsg/utils/TracyInstrumentation.h new file mode 100644 index 0000000000..dadaf2bdcb --- /dev/null +++ b/include/vsg/utils/TracyInstrumentation.h @@ -0,0 +1,256 @@ +#pragma once + +/* + +Copyright(c) 2023 Robert Osfield + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + */ + +#include +#include + +#include + +using namespace tracy; + +namespace vsg +{ + + class TracySettings : public Inherit + { + public: + uint32_t cpu_instumentation_level = 3; + uint32_t gpu_instumentation_level = 3; + }; + VSG_type_name(vsg::TracySettings); + +#ifdef TRACY_ENABLE + /// thread safe helper class for creating the Tracy VkCtx objects per Device. + class TracyContexts : public Inherit + { + public: + VkCtx* getOrCreateContext(CommandBuffer& commandBuffer) const + { + std::scoped_lock lock(mutex); + + ref_ptr device(commandBuffer.getDevice()); + auto& [ctx, requiresCollection] = ctxMap[device]; + if (!ctx) + { + auto queue = device->getQueue(commandBuffer.getCommandPool()->queueFamilyIndex, 0); + auto commandPool = CommandPool::create(device, queue->queueFamilyIndex(), VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); + auto temporaryCommandBuffer = commandPool->allocate(); + auto extensions = device->getInstance()->getExtensions(); + + if (device->supportsDeviceExtension("VK_EXT_calibrated_timestamps")) + { + ctx = TracyVkContextCalibrated(device->getPhysicalDevice()->vk(), device->vk(), queue->vk(), temporaryCommandBuffer->vk(), + extensions->vkGetPhysicalDeviceCalibrateableTimeDomainsEXT, extensions->vkGetCalibratedTimestampsEXT); + } + else + { + ctx = TracyVkContext(device->getPhysicalDevice()->vk(), device->vk(), queue->vk(), temporaryCommandBuffer->vk()); + } + requiresCollection = false; + } + + if (ctx && requiresCollection) + { + TracyVkCollect(ctx, commandBuffer.vk()); + requiresCollection = false; + } + + return ctx; + } + + void frameComplete() + { + std::scoped_lock lock(mutex); + for (auto itr = ctxMap.begin(); itr != ctxMap.end(); ++itr) + { + itr->second.second = true; + } + } + + mutable std::mutex mutex; + mutable std::map, std::pair> ctxMap; + + protected: + ~TracyContexts() + { + for (auto itr = ctxMap.begin(); itr != ctxMap.end(); ++itr) + { + TracyVkDestroy(itr->second.first); + } + } + + }; + VSG_type_name(vsg::TracyContexts); + + /// TracyInstrumentation provides integration between the Instrumentation system and the Tracy profiler + class TracyInstrumentation : public Inherit + { + public: + TracyInstrumentation() : + settings(TracySettings::create()), + contexts(TracyContexts::create()) + { + } + + TracyInstrumentation(TracyInstrumentation& parent) : + settings(parent.settings), + contexts(parent.contexts) + { + } + + ref_ptr settings; + ref_ptr contexts; + mutable VkCtx* ctx = nullptr; + bool requiresCollection = false; + + ref_ptr shareOrDuplicateForThreadSafety() override + { + return TracyInstrumentation::create(*this); + } + + void setThreadName(const std::string& name) const override + { + tracy::SetThreadName(name.c_str()); + } + + void enterFrame(const SourceLocation*, uint64_t&, FrameStamp&) const override {} + + void leaveFrame(const SourceLocation*, uint64_t&, FrameStamp&) const override + { + contexts->frameComplete(); + + FrameMark; + } + + void enter(const SourceLocation* slcloc, uint64_t& reference, const Object*) const override + { +# ifdef TRACY_ON_DEMAND + if (!GetProfiler().IsConnected() || (slcloc->level > settings->cpu_instumentation_level)) +# else + if (slcloc->level > settings->cpu_instumentation_level) +# endif + { + reference = 0; + return; + } + +# ifdef TRACY_ON_DEMAND + reference = GetProfiler().ConnectionId(); +# else + reference = 1; +# endif + + TracyQueuePrepare(QueueType::ZoneBegin); + MemWrite(&item->zoneBegin.time, Profiler::GetTime()); + MemWrite(&item->zoneBegin.srcloc, (uint64_t)slcloc); + TracyQueueCommit(zoneBeginThread); + } + + void leave(const SourceLocation*, uint64_t& reference, const Object*) const override + { +# ifdef TRACY_ON_DEMAND + if (reference == 0 || GetProfiler().ConnectionId() != reference) return; +# else + if (reference == 0) return; +# endif + + TracyQueuePrepare(QueueType::ZoneEnd); + MemWrite(&item->zoneEnd.time, Profiler::GetTime()); + TracyQueueCommit(zoneEndThread); + } + + void enterCommandBuffer(const SourceLocation* slcloc, uint64_t& reference, CommandBuffer& commandBuffer) const override + { + if (ctx = contexts->getOrCreateContext(commandBuffer)) + { + enter(slcloc, reference, commandBuffer, nullptr); + } + } + + void leaveCommandBuffer(const SourceLocation* slcloc, uint64_t& reference, CommandBuffer& commandBuffer) const override + { + if (ctx) + { + leave(slcloc, reference, commandBuffer, nullptr); + } + + ctx = nullptr; + } + + void enter(const SourceLocation* slcloc, uint64_t& reference, CommandBuffer& cmdbuf, const Object*) const override + { +# ifdef TRACY_ON_DEMAND + if (!ctx || !GetProfiler().IsConnected() || (slcloc->level > settings->gpu_instumentation_level)) +# else + if (!ctx || slcloc->level > settings->gpu_instumentation_level) +# endif + { + reference = 0; + return; + } + +# ifdef TRACY_ON_DEMAND + reference = GetProfiler().ConnectionId(); +# else + reference = 1; +# endif + + const auto queryId = ctx->NextQueryId(); + CONTEXT_VK_FUNCTION_WRAPPER(vkCmdWriteTimestamp(cmdbuf, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, ctx->GetQueryPool(), queryId)); + + auto item = Profiler::QueueSerial(); + MemWrite(&item->hdr.type, QueueType::GpuZoneBeginSerial); + MemWrite(&item->gpuZoneBegin.cpuTime, Profiler::GetTime()); + MemWrite(&item->gpuZoneBegin.srcloc, (uint64_t)slcloc); + MemWrite(&item->gpuZoneBegin.thread, GetThreadHandle()); + MemWrite(&item->gpuZoneBegin.queryId, uint16_t(queryId)); + MemWrite(&item->gpuZoneBegin.context, ctx->GetId()); + Profiler::QueueSerialFinish(); + } + + void leave(const SourceLocation*, uint64_t& reference, CommandBuffer& cmdbuf, const Object*) const override + { +# ifdef TRACY_ON_DEMAND + if (reference == 0 || GetProfiler().ConnectionId() != reference) return; +# else + if (reference == 0) return; +# endif + + const auto queryId = ctx->NextQueryId(); + CONTEXT_VK_FUNCTION_WRAPPER(vkCmdWriteTimestamp(cmdbuf, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, ctx->GetQueryPool(), queryId)); + + auto item = Profiler::QueueSerial(); + MemWrite(&item->hdr.type, QueueType::GpuZoneEndSerial); + MemWrite(&item->gpuZoneEnd.cpuTime, Profiler::GetTime()); + MemWrite(&item->gpuZoneEnd.thread, GetThreadHandle()); + MemWrite(&item->gpuZoneEnd.queryId, uint16_t(queryId)); + MemWrite(&item->gpuZoneEnd.context, ctx->GetId()); + Profiler::QueueSerialFinish(); + } + }; + VSG_type_name(vsg::TracyInstrumentation); +#else + class TracyInstrumentation : public Inherit + { + public: + TracyInstrumentation() + { + vsg::info("TracyInstrumentation not supported and the tracy's TRACY_ENABLE is set to OFF."); + } + + ref_ptr settings; + }; + VSG_type_name(vsg::TracyInstrumentation); +#endif +} // namespace vsg diff --git a/include/vsg/vk/CommandPool.h b/include/vsg/vk/CommandPool.h index 9c41a63007..2a68ba7b08 100644 --- a/include/vsg/vk/CommandPool.h +++ b/include/vsg/vk/CommandPool.h @@ -29,7 +29,10 @@ namespace vsg operator VkCommandPool() const { return _commandPool; } VkCommandPool vk() const { return _commandPool; } - void reset(VkCommandPoolResetFlags flags = VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT) const { vkResetCommandPool(*_device, _commandPool, flags); } + const uint32_t queueFamilyIndex; + const VkCommandPoolCreateFlags flags; + + void reset(VkCommandPoolResetFlags reset_flags = VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT) const { vkResetCommandPool(*_device, _commandPool, reset_flags); } /// allocate CommandBuffer from CommandPool ref_ptr allocate(VkCommandBufferLevel level = VK_COMMAND_BUFFER_LEVEL_PRIMARY); diff --git a/include/vsg/vk/Context.h b/include/vsg/vk/Context.h index 0e3e15118b..51bf078176 100644 --- a/include/vsg/vk/Context.h +++ b/include/vsg/vk/Context.h @@ -15,11 +15,15 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include +#include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -27,10 +31,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include -#include -#include -#include - namespace vsg { // forward declare @@ -119,6 +119,9 @@ namespace vsg // ShaderCompiler ref_ptr shaderCompiler; + /// Hook for assigning Instrumentation to enable profiling + ref_ptr instrumentation; + // transfer data settings ref_ptr graphicsQueue; ref_ptr commandPool; diff --git a/include/vsg/vk/Device.h b/include/vsg/vk/Device.h index 7595473d7a..be78f4b989 100644 --- a/include/vsg/vk/Device.h +++ b/include/vsg/vk/Device.h @@ -76,6 +76,12 @@ namespace vsg /// device-level core functionality can be used if both VkInstance and VkPhysicalDevice support the Vulkan version that provides it. bool supportsApiVersion(uint32_t version) const; + /// list of enabled extensions when the Device was created + const Names enabledExtensions; + + /// return true if Device was created with specified extension + bool supportsDeviceExtension(const char* extensionName) const; + protected: virtual ~Device(); diff --git a/include/vsg/vk/DeviceExtensions.h b/include/vsg/vk/DeviceExtensions.h index 1bc914431d..7868f63540 100644 --- a/include/vsg/vk/DeviceExtensions.h +++ b/include/vsg/vk/DeviceExtensions.h @@ -18,12 +18,8 @@ namespace vsg { class Device; - extern VSG_DECLSPEC bool isExtensionSupported(const char* extensionName); - - extern VSG_DECLSPEC bool isExtensionListSupported(const Names& extensionList); - /// Extensions manages a set of Vulkan extension function pointers. - /// The vsg::Device "has a" Extensions object that can be accessed via device->getExtensions(). + /// The vsg::Device "has a" DeviceExtensions object that can be accessed via device->getExtensions(). class VSG_DECLSPEC DeviceExtensions : public Inherit { public: diff --git a/include/vsg/vk/Instance.h b/include/vsg/vk/Instance.h index 30ac17c9ac..4d4bb345f8 100644 --- a/include/vsg/vk/Instance.h +++ b/include/vsg/vk/Instance.h @@ -25,12 +25,16 @@ namespace vsg class Surface; using Names = std::vector; + using ExtensionProperties = std::vector; using PhysicalDeviceTypes = std::vector; using InstanceLayerProperties = std::vector; - using InstanceExtensionProperties = std::vector; + /// wrapper for vkEnumerateInstanceExtensionProperties - extern VSG_DECLSPEC InstanceExtensionProperties enumerateInstanceExtensionProperties(const char* pLayerName = nullptr); + extern VSG_DECLSPEC ExtensionProperties enumerateInstanceExtensionProperties(const char* pLayerName = nullptr); + + /// return true if the specified instance extension is supported + extern VSG_DECLSPEC bool isExtensionSupported(const char* extensionName, const char* pLayerName = nullptr); /// wrapper for vkEnumerateInstanceLayerProperties extern VSG_DECLSPEC InstanceLayerProperties enumerateInstanceLayerProperties(); diff --git a/include/vsg/vk/InstanceExtensions.h b/include/vsg/vk/InstanceExtensions.h index e1df36d658..c94bd17f32 100644 --- a/include/vsg/vk/InstanceExtensions.h +++ b/include/vsg/vk/InstanceExtensions.h @@ -19,7 +19,7 @@ namespace vsg class Instance; /// Extensions manages a set of Vulkan extension function pointers. - /// The vsg::Device "has a" Extensions object that can be accessed via device->getExtensions(). + /// The vsg::Instance "has a" InstanceExtensions object that can be accessed via instance->getExtensions(). class VSG_DECLSPEC InstanceExtensions : public Inherit { public: @@ -37,6 +37,10 @@ namespace vsg PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT = nullptr; PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT = nullptr; PFN_vkSubmitDebugUtilsMessageEXT vkSubmitDebugUtilsMessageEXT = nullptr; + + // VK_EXT_calibrated_timestamps + PFN_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT vkGetPhysicalDeviceCalibrateableTimeDomainsEXT = nullptr; + PFN_vkGetCalibratedTimestampsEXT vkGetCalibratedTimestampsEXT = nullptr; }; VSG_type_name(vsg::InstanceExtensions); diff --git a/include/vsg/vk/PhysicalDevice.h b/include/vsg/vk/PhysicalDevice.h index 36fbb7e12d..b5f27f805e 100644 --- a/include/vsg/vk/PhysicalDevice.h +++ b/include/vsg/vk/PhysicalDevice.h @@ -73,7 +73,7 @@ namespace vsg } /// Call vkEnumerateDeviceExtensionProperties to enumerate extension properties. - std::vector enumerateDeviceExtensionProperties(const char* pLayerName = nullptr); + ExtensionProperties enumerateDeviceExtensionProperties(const char* pLayerName = nullptr); /// return true if the extension is supported by physicalDevice bool supportsDeviceExtension(const char* extensionName); diff --git a/src/vsg/CMakeLists.txt b/src/vsg/CMakeLists.txt index 6932475556..6ee22bb697 100644 --- a/src/vsg/CMakeLists.txt +++ b/src/vsg/CMakeLists.txt @@ -42,6 +42,7 @@ set(SOURCES nodes/StateGroup.cpp nodes/Light.cpp nodes/TileDatabase.cpp + nodes/InstrumentationNode.cpp commands/BindIndexBuffer.cpp commands/BindVertexBuffers.cpp @@ -223,6 +224,8 @@ set(SOURCES utils/ShaderCompiler.cpp utils/ComputeBounds.cpp utils/Intersector.cpp + utils/Instrumentation.cpp + utils/GpuAnnotation.cpp utils/LineSegmentIntersector.cpp utils/LoadPagedLOD.cpp ) diff --git a/src/vsg/app/CommandGraph.cpp b/src/vsg/app/CommandGraph.cpp index dd4cf4b502..cf6df713fa 100644 --- a/src/vsg/app/CommandGraph.cpp +++ b/src/vsg/app/CommandGraph.cpp @@ -22,6 +22,7 @@ using namespace vsg; CommandGraph::CommandGraph() { + CPU_INSTRUMENTATION_L1(instrumentation); } CommandGraph::CommandGraph(ref_ptr in_device, int family) : @@ -29,12 +30,15 @@ CommandGraph::CommandGraph(ref_ptr in_device, int family) : queueFamily(family), presentFamily(-1) { + CPU_INSTRUMENTATION_L1(instrumentation); } CommandGraph::CommandGraph(ref_ptr in_window, ref_ptr child) : window(in_window), device(in_window->getOrCreateDevice()) { + CPU_INSTRUMENTATION_L1(instrumentation); + VkQueueFlags queueFlags = VK_QUEUE_GRAPHICS_BIT; if (window->traits()) queueFlags = window->traits()->queueFlags; @@ -45,6 +49,7 @@ CommandGraph::CommandGraph(ref_ptr in_window, ref_ptr child) : CommandGraph::~CommandGraph() { + CPU_INSTRUMENTATION_L1(instrumentation); } VkCommandBufferLevel CommandGraph::level() const @@ -56,8 +61,21 @@ void CommandGraph::reset() { } +ref_ptr CommandGraph::getOrCreateRecordTraversal() +{ + CPU_INSTRUMENTATION_L1(instrumentation); + + if (!recordTraversal) + { + recordTraversal = RecordTraversal::create(maxSlot); + } + return recordTraversal; +} + void CommandGraph::record(ref_ptr recordedCommandBuffers, ref_ptr frameStamp, ref_ptr databasePager) { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "CommandGraph record", COLOR_RECORD_L1); + if (window && !window->visible()) { return; @@ -112,7 +130,10 @@ void CommandGraph::record(ref_ptr recordedCommandBuffers vkBeginCommandBuffer(vk_commandBuffer, &beginInfo); - traverse(*recordTraversal); + { + COMMAND_BUFFER_INSTRUMENTATION(instrumentation, *commandBuffer, "CommandGraph record", COLOR_RECORD) + traverse(*recordTraversal); + } vkEndCommandBuffer(vk_commandBuffer); diff --git a/src/vsg/app/CompileManager.cpp b/src/vsg/app/CompileManager.cpp index f52cfd70c3..038a9c0a05 100644 --- a/src/vsg/app/CompileManager.cpp +++ b/src/vsg/app/CompileManager.cpp @@ -150,6 +150,17 @@ void CompileManager::add(const Viewer& viewer, const ResourceRequirements& resou } } +void CompileManager::assignInstrumentation(ref_ptr in_instrumentation) +{ + auto cts = takeCompileTraversals(numCompileTraversals); + for (auto& ct : cts) + { + ct->assignInstrumentation(in_instrumentation); + + compileTraversals->add(ct); + } +} + CompileResult CompileManager::compile(ref_ptr object, ContextSelectionFunction contextSelection) { CollectResourceRequirements collectRequirements; diff --git a/src/vsg/app/CompileTraversal.cpp b/src/vsg/app/CompileTraversal.cpp index f0fb9f1ce2..042e0f8ef7 100644 --- a/src/vsg/app/CompileTraversal.cpp +++ b/src/vsg/app/CompileTraversal.cpp @@ -63,6 +63,7 @@ void CompileTraversal::add(ref_ptr device, const ResourceRequirements& r { auto queueFamily = device->getPhysicalDevice()->getQueueFamily(queueFlags); auto context = Context::create(device, resourceRequirements); + context->instrumentation = instrumentation; context->commandPool = CommandPool::create(device, queueFamily, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); context->graphicsQueue = device->getQueue(queueFamily, queueFamilyIndex); contexts.push_back(context); @@ -74,6 +75,7 @@ void CompileTraversal::add(Window& window, ref_ptr viewport, cons auto renderPass = window.getOrCreateRenderPass(); auto queueFamily = device->getPhysicalDevice()->getQueueFamily(queueFlags); auto context = Context::create(device, resourceRequirements); + context->instrumentation = instrumentation; context->renderPass = renderPass; context->commandPool = CommandPool::create(device, queueFamily, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); context->graphicsQueue = device->getQueue(queueFamily, queueFamilyIndex); @@ -94,6 +96,7 @@ void CompileTraversal::add(Window& window, ref_ptr view, const ResourceReq auto renderPass = window.getOrCreateRenderPass(); auto queueFamily = device->getPhysicalDevice()->getQueueFamily(queueFlags); auto context = Context::create(device, resourceRequirements); + context->instrumentation = instrumentation; context->renderPass = renderPass; context->commandPool = vsg::CommandPool::create(device, queueFamily, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); context->graphicsQueue = device->getQueue(queueFamily, queueFamilyIndex); @@ -122,6 +125,7 @@ void CompileTraversal::add(Framebuffer& framebuffer, ref_ptr view, const R auto renderPass = framebuffer.getRenderPass(); auto queueFamily = device->getPhysicalDevice()->getQueueFamily(VK_QUEUE_GRAPHICS_BIT); auto context = Context::create(device, resourceRequirements); + context->instrumentation = instrumentation; context->renderPass = renderPass; context->commandPool = vsg::CommandPool::create(device, queueFamily, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); context->graphicsQueue = device->getQueue(queueFamily, queueFamilyIndex); @@ -146,6 +150,8 @@ void CompileTraversal::add(Framebuffer& framebuffer, ref_ptr view, const R void CompileTraversal::add(const Viewer& viewer, const ResourceRequirements& resourceRequirements) { + if (viewer.instrumentation) instrumentation = viewer.instrumentation; + struct AddViews : public Visitor { CompileTraversal* ct = nullptr; @@ -208,13 +214,26 @@ void CompileTraversal::addViewDependentState(ViewDependentState& viewDependentSt } } +void CompileTraversal::assignInstrumentation(ref_ptr in_instrumentation) +{ + instrumentation = in_instrumentation; + for (auto& context : contexts) + { + context->instrumentation = shareOrDuplicateForThreadSafety(instrumentation); + } +} + void CompileTraversal::apply(Object& object) { + CPU_INSTRUMENTATION_L2_NC(instrumentation, "CompileTraversal Object", COLOR_COMPILE); + object.traverse(*this); } void CompileTraversal::apply(Compilable& node) { + CPU_INSTRUMENTATION_L3_NC(instrumentation, "CompileTraversal Compilable", COLOR_COMPILE); + for (auto& context : contexts) { node.compile(*context); @@ -223,6 +242,8 @@ void CompileTraversal::apply(Compilable& node) void CompileTraversal::apply(Commands& commands) { + CPU_INSTRUMENTATION_L3_NC(instrumentation, "CompileTraversal Commands", COLOR_COMPILE); + for (auto& context : contexts) { commands.compile(*context); @@ -231,6 +252,8 @@ void CompileTraversal::apply(Commands& commands) void CompileTraversal::apply(StateGroup& stateGroup) { + CPU_INSTRUMENTATION_L2_NC(instrumentation, "CompileTraversal StateGroup", COLOR_COMPILE); + for (auto& context : contexts) { stateGroup.compile(*context); @@ -240,6 +263,8 @@ void CompileTraversal::apply(StateGroup& stateGroup) void CompileTraversal::apply(Geometry& geometry) { + CPU_INSTRUMENTATION_L3_NC(instrumentation, "CompileTraversal Geometry", COLOR_COMPILE); + for (auto& context : contexts) { geometry.compile(*context); @@ -249,6 +274,8 @@ void CompileTraversal::apply(Geometry& geometry) void CompileTraversal::apply(CommandGraph& commandGraph) { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "CompileTraversal CommandGraph", COLOR_COMPILE); + for (auto& context : contexts) { if (context->resourceRequirements.maxSlot > commandGraph.maxSlot) @@ -262,6 +289,8 @@ void CompileTraversal::apply(CommandGraph& commandGraph) void CompileTraversal::apply(RenderGraph& renderGraph) { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "CompileTraversal RenderGraph", COLOR_COMPILE); + for (auto& context : contexts) { context->renderPass = renderGraph.getRenderPass(); @@ -294,6 +323,8 @@ void CompileTraversal::apply(RenderGraph& renderGraph) void CompileTraversal::apply(View& view) { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "CompileTraversal View", COLOR_COMPILE); + for (auto& context : contexts) { // if context is associated with a view make sure we only apply it if it matches with view, otherwise we skip this context @@ -333,6 +364,8 @@ void CompileTraversal::apply(View& view) bool CompileTraversal::record() { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "CompileTraversal record", COLOR_COMPILE); + bool recorded = false; for (auto& context : contexts) { @@ -343,6 +376,8 @@ bool CompileTraversal::record() void CompileTraversal::waitForCompletion() { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "CompileTraversal waitForCompletion", COLOR_COMPILE); + for (auto& context : contexts) { context->waitForCompletion(); diff --git a/src/vsg/app/RecordAndSubmitTask.cpp b/src/vsg/app/RecordAndSubmitTask.cpp index 404188ae7b..890e87f12a 100644 --- a/src/vsg/app/RecordAndSubmitTask.cpp +++ b/src/vsg/app/RecordAndSubmitTask.cpp @@ -21,6 +21,8 @@ using namespace vsg; RecordAndSubmitTask::RecordAndSubmitTask(Device* in_device, uint32_t numBuffers) : device(in_device) { + CPU_INSTRUMENTATION_L1(instrumentation); + _currentFrameIndex = numBuffers; // numBuffers is used to signify unset value for (uint32_t i = 0; i < numBuffers; ++i) { @@ -42,6 +44,8 @@ RecordAndSubmitTask::RecordAndSubmitTask(Device* in_device, uint32_t numBuffers) void RecordAndSubmitTask::advance() { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "RecordAndSubmitTask advance", COLOR_VIEWER); + if (_currentFrameIndex >= _indices.size()) { // first frame so set to 0 @@ -80,6 +84,8 @@ Fence* RecordAndSubmitTask::fence(size_t relativeFrameIndex) VkResult RecordAndSubmitTask::submit(ref_ptr frameStamp) { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "RecordAndSubmitTask submit", COLOR_RECORD); + if (VkResult result = start(); result != VK_SUCCESS) return result; if (earlyTransferTask) @@ -96,6 +102,8 @@ VkResult RecordAndSubmitTask::submit(ref_ptr frameStamp) VkResult RecordAndSubmitTask::start() { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "RecordAndSubmitTask start", COLOR_RECORD); + if (earlyTransferTask) earlyTransferTask->currentTransferCompletedSemaphore = {}; if (lateTransferTask) lateTransferTask->currentTransferCompletedSemaphore = {}; @@ -112,6 +120,8 @@ VkResult RecordAndSubmitTask::start() VkResult RecordAndSubmitTask::record(ref_ptr recordedCommandBuffers, ref_ptr frameStamp) { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "RecordAndSubmitTask record", COLOR_RECORD); + for (auto& commandGraph : commandGraphs) { commandGraph->record(recordedCommandBuffers, frameStamp, databasePager); @@ -122,6 +132,8 @@ VkResult RecordAndSubmitTask::record(ref_ptr recordedCom VkResult RecordAndSubmitTask::finish(ref_ptr recordedCommandBuffers) { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "RecordAndSubmitTask finish", COLOR_RECORD); + if (lateTransferTask) { if (VkResult result = lateTransferTask->transferDynamicData(); result != VK_SUCCESS) return result; @@ -209,6 +221,21 @@ VkResult RecordAndSubmitTask::finish(ref_ptr recordedCom return queue->submit(submitInfo, current_fence); } +void RecordAndSubmitTask::assignInstrumentation(ref_ptr in_instrumentation) +{ + instrumentation = in_instrumentation; + + if (databasePager) databasePager->assignInstrumentation(instrumentation); + if (earlyTransferTask) earlyTransferTask->instrumentation = shareOrDuplicateForThreadSafety(instrumentation); + if (lateTransferTask) lateTransferTask->instrumentation = shareOrDuplicateForThreadSafety(instrumentation); + + for (auto cg : commandGraphs) + { + cg->instrumentation = shareOrDuplicateForThreadSafety(instrumentation); + cg->getOrCreateRecordTraversal()->instrumentation = cg->instrumentation; + } +} + void vsg::updateTasks(RecordAndSubmitTasks& tasks, ref_ptr compileManager, const CompileResult& compileResult) { //info("vsg::updateTasks(RecordAndSubmitTasks& tasks..) compileResult.maxSlot = ", compileResult.maxSlot); diff --git a/src/vsg/app/RecordTraversal.cpp b/src/vsg/app/RecordTraversal.cpp index d2b57af527..993eec3f12 100644 --- a/src/vsg/app/RecordTraversal.cpp +++ b/src/vsg/app/RecordTraversal.cpp @@ -24,6 +24,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include #include #include #include @@ -32,6 +33,9 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include +#include +#include #include #include #include @@ -39,6 +43,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include +#include + using namespace vsg; #define INLINE_TRAVERSE 0 @@ -46,6 +52,8 @@ using namespace vsg; RecordTraversal::RecordTraversal(uint32_t in_maxSlot, std::set in_bins) : _state(new State(in_maxSlot)) { + CPU_INSTRUMENTATION_L1_C(instrumentation, COLOR_RECORD); + _minimumBinNumber = 0; int32_t maximumBinNumber = 0; for (auto& bin : in_bins) @@ -64,6 +72,7 @@ RecordTraversal::RecordTraversal(uint32_t in_maxSlot, std::set in_bins) : RecordTraversal::~RecordTraversal() { + CPU_INSTRUMENTATION_L2(instrumentation); } CommandBuffer* RecordTraversal::getCommandBuffer() @@ -91,6 +100,8 @@ void RecordTraversal::setDatabasePager(DatabasePager* dp) void RecordTraversal::clearBins() { + CPU_INSTRUMENTATION_L2_NC(instrumentation, "RecordTraversal clearBins", COLOR_RECORD_L2); + for (auto& bin : _bins) { if (bin) bin->clear(); @@ -99,12 +110,16 @@ void RecordTraversal::clearBins() void RecordTraversal::apply(const Object& object) { + // GPU_INSTRUMENTATION_L2_NCO(instrumentation, *getCommandBuffer(), "Object", COLOR_RECORD_L2, &object); + //debug("Visiting Object"); object.traverse(*this); } void RecordTraversal::apply(const Group& group) { + GPU_INSTRUMENTATION_L2_NCO(instrumentation, *getCommandBuffer(), "Group", COLOR_RECORD_L2, &group); + //debug("Visiting Group"); #if INLINE_TRAVERSE vsg::Group::t_traverse(group, *this); @@ -115,6 +130,8 @@ void RecordTraversal::apply(const Group& group) void RecordTraversal::apply(const QuadGroup& quadGroup) { + GPU_INSTRUMENTATION_L2_NCO(instrumentation, *getCommandBuffer(), "QuadGroup", COLOR_RECORD_L2, &quadGroup); + //debug("Visiting QuadGroup"); #if INLINE_TRAVERSE vsg::QuadGroup::t_traverse(quadGroup, *this); @@ -125,6 +142,8 @@ void RecordTraversal::apply(const QuadGroup& quadGroup) void RecordTraversal::apply(const LOD& lod) { + GPU_INSTRUMENTATION_L2_NCO(instrumentation, *getCommandBuffer(), "LOD", COLOR_RECORD_L2, &lod); + const auto& sphere = lod.bound; // check if lod bounding sphere is in view frustum. @@ -148,6 +167,8 @@ void RecordTraversal::apply(const LOD& lod) void RecordTraversal::apply(const PagedLOD& plod) { + GPU_INSTRUMENTATION_L2_NCO(instrumentation, *getCommandBuffer(), "PagedLOD", COLOR_PAGER, &plod); + const auto& sphere = plod.bound; auto frameCount = _frameStamp->frameCount; @@ -224,8 +245,17 @@ void RecordTraversal::apply(const PagedLOD& plod) } } +void RecordTraversal::apply(const TileDatabase& tileDatabase) +{ + GPU_INSTRUMENTATION_L2_NCO(instrumentation, *getCommandBuffer(), "TileDatabase", COLOR_RECORD_L2, &tileDatabase); + + tileDatabase.traverse(*this); +} + void RecordTraversal::apply(const CullGroup& cullGroup) { + GPU_INSTRUMENTATION_L2_NCO(instrumentation, *getCommandBuffer(), "CullGroup", COLOR_RECORD_L2, &cullGroup); + if (_state->intersect(cullGroup.bound)) { // debug("Passed node"); @@ -235,6 +265,8 @@ void RecordTraversal::apply(const CullGroup& cullGroup) void RecordTraversal::apply(const CullNode& cullNode) { + GPU_INSTRUMENTATION_L2_NCO(instrumentation, *getCommandBuffer(), "CullNode", COLOR_RECORD_L2, &cullNode); + if (_state->intersect(cullNode.bound)) { //debug("Passed node"); @@ -242,8 +274,23 @@ void RecordTraversal::apply(const CullNode& cullNode) } } +void RecordTraversal::apply(const Switch& sw) +{ + GPU_INSTRUMENTATION_L2_NCO(instrumentation, *getCommandBuffer(), "Switch", COLOR_RECORD_L2, &sw); + + for (auto& child : sw.children) + { + if ((traversalMask & (overrideMask | child.mask)) != MASK_OFF) + { + child.node->accept(*this); + } + } +} + void RecordTraversal::apply(const DepthSorted& depthSorted) { + CPU_INSTRUMENTATION_L2_NCO(instrumentation, "DepthSorted", COLOR_RECORD_L2, &depthSorted); + if (_state->intersect(depthSorted.bound)) { const auto& mv = _state->modelviewMatrixStack.top(); @@ -254,48 +301,76 @@ void RecordTraversal::apply(const DepthSorted& depthSorted) } } -void RecordTraversal::apply(const Switch& sw) +void RecordTraversal::apply(const VertexDraw& vd) { - for (auto& child : sw.children) - { - if ((traversalMask & (overrideMask | child.mask)) != MASK_OFF) - { - child.node->accept(*this); - } - } + GPU_INSTRUMENTATION_L3_NCO(instrumentation, *getCommandBuffer(), "VertexDraw", COLOR_GPU, &vd); + + //debug("Visiting VertexDraw"); + _state->record(); + vd.record(*(_state->_commandBuffer)); +} + +void RecordTraversal::apply(const VertexIndexDraw& vid) +{ + GPU_INSTRUMENTATION_L3_NCO(instrumentation, *getCommandBuffer(), "VertexIndexDraw", COLOR_GPU, &vid); + + //debug("Visiting VertexIndexDraw"); + _state->record(); + vid.record(*(_state->_commandBuffer)); +} + +void RecordTraversal::apply(const Geometry& geometry) +{ + GPU_INSTRUMENTATION_L3_NCO(instrumentation, *getCommandBuffer(), "Geometry", COLOR_GPU, &geometry); + + //debug("Visiting Geometry"); + _state->record(); + geometry.record(*(_state->_commandBuffer)); } -void RecordTraversal::apply(const Light& /*light*/) +void RecordTraversal::apply(const Light&) { + CPU_INSTRUMENTATION_L2(instrumentation); + //debug("RecordTraversal::apply(Light) ", light.className()); } void RecordTraversal::apply(const AmbientLight& light) { + CPU_INSTRUMENTATION_L2_O(instrumentation, &light); + //debug("RecordTraversal::apply(AmbientLight) ", light.className()); if (_viewDependentState) _viewDependentState->ambientLights.emplace_back(_state->modelviewMatrixStack.top(), &light); } void RecordTraversal::apply(const DirectionalLight& light) { + CPU_INSTRUMENTATION_L2_O(instrumentation, &light); + //debug("RecordTraversal::apply(DirectionalLight) ", light.className()); if (_viewDependentState) _viewDependentState->directionalLights.emplace_back(_state->modelviewMatrixStack.top(), &light); } void RecordTraversal::apply(const PointLight& light) { + CPU_INSTRUMENTATION_L2_O(instrumentation, &light); + //debug("RecordTraversal::apply(PointLight) ", light.className()); if (_viewDependentState) _viewDependentState->pointLights.emplace_back(_state->modelviewMatrixStack.top(), &light); } void RecordTraversal::apply(const SpotLight& light) { + CPU_INSTRUMENTATION_L2_O(instrumentation, &light); + //debug("RecordTraversal::apply(SpotLight) ", light.className()); if (_viewDependentState) _viewDependentState->spotLights.emplace_back(_state->modelviewMatrixStack.top(), &light); } void RecordTraversal::apply(const StateGroup& stateGroup) { + GPU_INSTRUMENTATION_L2_NCO(instrumentation, *getCommandBuffer(), "StateGroup", COLOR_RECORD_L2, &stateGroup); + //debug("Visiting StateGroup"); for (auto& command : stateGroup.stateCommands) @@ -315,6 +390,8 @@ void RecordTraversal::apply(const StateGroup& stateGroup) void RecordTraversal::apply(const Transform& transform) { + GPU_INSTRUMENTATION_L2_NCO(instrumentation, *getCommandBuffer(), "Transform", COLOR_RECORD_L2, &transform); + _state->modelviewMatrixStack.push(transform); _state->dirty = true; @@ -335,6 +412,8 @@ void RecordTraversal::apply(const Transform& transform) void RecordTraversal::apply(const MatrixTransform& mt) { + GPU_INSTRUMENTATION_L2_NCO(instrumentation, *getCommandBuffer(), "MatrixTransform", COLOR_RECORD_L2, &mt); + _state->modelviewMatrixStack.push(mt); _state->dirty = true; @@ -356,6 +435,8 @@ void RecordTraversal::apply(const MatrixTransform& mt) // Vulkan nodes void RecordTraversal::apply(const Commands& commands) { + GPU_INSTRUMENTATION_L3_NCO(instrumentation, *getCommandBuffer(), "Commands", COLOR_GPU, &commands); + _state->record(); for (auto& command : commands.children) { @@ -365,13 +446,25 @@ void RecordTraversal::apply(const Commands& commands) void RecordTraversal::apply(const Command& command) { + GPU_INSTRUMENTATION_L3_NCO(instrumentation, *getCommandBuffer(), "Command", COLOR_GPU, &command); + //debug("Visiting Command"); _state->record(); command.record(*(_state->_commandBuffer)); } +void RecordTraversal::apply(const Bin& bin) +{ + GPU_INSTRUMENTATION_L1_NCO(instrumentation, *getCommandBuffer(), "Bin", COLOR_RECORD_L1, &bin); + + //debug("Visiting Bin"); + bin.traverse(*this); +} + void RecordTraversal::apply(const View& view) { + GPU_INSTRUMENTATION_L1_NCO(instrumentation, *getCommandBuffer(), "View", COLOR_RECORD_L1, &view); + // note, View::accept() updates the RecordTraversal's traversalMask auto cached_traversalMask = _state->_commandBuffer->traversalMask; _state->_commandBuffer->traversalMask = traversalMask; @@ -459,6 +552,8 @@ void RecordTraversal::apply(const View& view) void RecordTraversal::apply(const CommandGraph& commandGraph) { + GPU_INSTRUMENTATION_L1_NCO(instrumentation, *getCommandBuffer(), "RecordTraversal CommandGraph", COLOR_RECORD_L1, &commandGraph); + if (recordedCommandBuffers) { auto cg = const_cast(&commandGraph); diff --git a/src/vsg/app/RenderGraph.cpp b/src/vsg/app/RenderGraph.cpp index 43a05b6e21..7166518ed8 100644 --- a/src/vsg/app/RenderGraph.cpp +++ b/src/vsg/app/RenderGraph.cpp @@ -102,6 +102,8 @@ VkExtent2D RenderGraph::getExtent() const void RenderGraph::accept(RecordTraversal& recordTraversal) const { + GPU_INSTRUMENTATION_L1_NC(recordTraversal.instrumentation, *recordTraversal.getCommandBuffer(), "RenderGraph", COLOR_RECORD_L1); + auto extent = getExtent(); if (previous_extent.width == invalid_dimension || previous_extent.height == invalid_dimension || !windowResizeHandler) { diff --git a/src/vsg/app/TransferTask.cpp b/src/vsg/app/TransferTask.cpp index 78ec3b0a4f..efb089a4a3 100644 --- a/src/vsg/app/TransferTask.cpp +++ b/src/vsg/app/TransferTask.cpp @@ -21,6 +21,8 @@ using namespace vsg; TransferTask::TransferTask(Device* in_device, uint32_t numBuffers) : device(in_device) { + CPU_INSTRUMENTATION_L1(instrumentation); + _currentFrameIndex = numBuffers; // numBuffers is used to signify unset value for (uint32_t i = 0; i < numBuffers; ++i) { @@ -32,6 +34,8 @@ TransferTask::TransferTask(Device* in_device, uint32_t numBuffers) : void TransferTask::advance() { + CPU_INSTRUMENTATION_L1(instrumentation); + if (_currentFrameIndex >= _indices.size()) { // first frame so set to 0 @@ -65,12 +69,16 @@ bool TransferTask::containsDataToTransfer() const void TransferTask::assign(const ResourceRequirements::DynamicData& dynamicData) { + CPU_INSTRUMENTATION_L1(instrumentation); + assign(dynamicData.bufferInfos); assign(dynamicData.imageInfos); } void TransferTask::assign(const BufferInfoList& bufferInfoList) { + CPU_INSTRUMENTATION_L1(instrumentation); + for (auto& bufferInfo : bufferInfoList) { _dynamicDataMap[bufferInfo->buffer][bufferInfo->offset] = bufferInfo; @@ -97,6 +105,8 @@ void TransferTask::assign(const BufferInfoList& bufferInfoList) void TransferTask::_transferBufferInfos(VkCommandBuffer vk_commandBuffer, Frame& frame, VkDeviceSize& offset) { + CPU_INSTRUMENTATION_L1(instrumentation); + Logger::Level level = Logger::LOGGER_DEBUG; //level = Logger::LOGGER_INFO; @@ -171,6 +181,8 @@ void TransferTask::_transferBufferInfos(VkCommandBuffer vk_commandBuffer, Frame& void TransferTask::assign(const ImageInfoList& imageInfoList) { + CPU_INSTRUMENTATION_L1(instrumentation); + Logger::Level level = Logger::LOGGER_DEBUG; //level = Logger::LOGGER_INFO; @@ -203,6 +215,8 @@ void TransferTask::assign(const ImageInfoList& imageInfoList) void TransferTask::_transferImageInfos(VkCommandBuffer vk_commandBuffer, Frame& frame, VkDeviceSize& offset) { + CPU_INSTRUMENTATION_L1(instrumentation); + Logger::Level level = Logger::LOGGER_DEBUG; //level = Logger::LOGGER_INFO; @@ -231,6 +245,8 @@ void TransferTask::_transferImageInfos(VkCommandBuffer vk_commandBuffer, Frame& void TransferTask::_transferImageInfo(VkCommandBuffer vk_commandBuffer, Frame& frame, VkDeviceSize& offset, ImageInfo& imageInfo) { + CPU_INSTRUMENTATION_L1(instrumentation); + Logger::Level level = Logger::LOGGER_DEBUG; //level = Logger::LOGGER_INFO; @@ -315,6 +331,8 @@ void TransferTask::_transferImageInfo(VkCommandBuffer vk_commandBuffer, Frame& f VkResult TransferTask::transferDynamicData() { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "transferDynamicData", COLOR_RECORD); + Logger::Level level = Logger::LOGGER_DEBUG; //level = Logger::LOGGER_INFO; @@ -378,10 +396,13 @@ VkResult TransferTask::transferDynamicData() vkBeginCommandBuffer(vk_commandBuffer, &beginInfo); VkDeviceSize offset = 0; + { + COMMAND_BUFFER_INSTRUMENTATION(instrumentation, *commandBuffer, "transferDynamicData", COLOR_GPU) - // transfer the modified BufferInfo and ImageInfo - _transferBufferInfos(vk_commandBuffer, frame, offset); - _transferImageInfos(vk_commandBuffer, frame, offset); + // transfer the modified BufferInfo and ImageInfo + _transferBufferInfos(vk_commandBuffer, frame, offset); + _transferImageInfos(vk_commandBuffer, frame, offset); + } vkEndCommandBuffer(vk_commandBuffer); diff --git a/src/vsg/app/Viewer.cpp b/src/vsg/app/Viewer.cpp index 987e7527ad..125dc9beb0 100644 --- a/src/vsg/app/Viewer.cpp +++ b/src/vsg/app/Viewer.cpp @@ -32,10 +32,13 @@ Viewer::Viewer() : status(vsg::ActivityStatus::create()), _start_point(clock::now()) { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "Viewer costructor", COLOR_VIEWER); } Viewer::~Viewer() { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "Viewer destructor", COLOR_VIEWER); + stopThreading(); // don't destroy viewer while devices are still active @@ -44,6 +47,8 @@ Viewer::~Viewer() void Viewer::deviceWaitIdle() const { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "Viewer deviceWaitIdle", COLOR_VIEWER); + std::set devices; for (auto& window : _windows) { @@ -96,6 +101,8 @@ void Viewer::removeWindow(ref_ptr window) void Viewer::close() { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "Viewer close", COLOR_VIEWER); + _close = true; status->set(false); @@ -104,6 +111,8 @@ void Viewer::close() bool Viewer::active() const { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "Viewer active", COLOR_VIEWER); + bool viewerIsActive = !_close; if (viewerIsActive) { @@ -127,6 +136,8 @@ bool Viewer::active() const bool Viewer::pollEvents(bool discardPreviousEvents) { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "Viewer pollEvents", COLOR_UPDATE); + bool result = false; if (discardPreviousEvents) _events.clear(); @@ -140,8 +151,14 @@ bool Viewer::pollEvents(bool discardPreviousEvents) bool Viewer::advanceToNextFrame() { + static constexpr SourceLocation s_frame_source_location{"Viewer advanceToNextFrame", VsgFunctionName, __FILE__, __LINE__, COLOR_VIEWER, 1}; + uint64_t reference = 0; + if (!active()) return false; + // signal to instrumentation the end of the previous frame + if (instrumentation && _frameStamp) instrumentation->leaveFrame(&s_frame_source_location, reference, *_frameStamp); + // poll all the windows for events. pollEvents(true); @@ -153,23 +170,21 @@ bool Viewer::advanceToNextFrame() { // first frame, initialize to frame count and indices to 0 _frameStamp = FrameStamp::create(time, 0); - - for (auto& task : recordAndSubmitTasks) - { - task->advance(); - } } else { // after first frame so increment frame count and indices _frameStamp = FrameStamp::create(time, _frameStamp->frameCount + 1); + } - for (auto& task : recordAndSubmitTasks) - { - task->advance(); - } + for (auto& task : recordAndSubmitTasks) + { + task->advance(); } + // signal to instrumentation the start of frame + if (instrumentation) instrumentation->enterFrame(&s_frame_source_location, reference, *_frameStamp); + // create an event for the new frame. _events.emplace_back(new FrameEvent(_frameStamp)); @@ -178,6 +193,8 @@ bool Viewer::advanceToNextFrame() bool Viewer::acquireNextFrame() { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "Viewer acquireNextFrame", COLOR_VIEWER); + if (_close) return false; VkResult result = VK_SUCCESS; @@ -216,6 +233,8 @@ bool Viewer::acquireNextFrame() VkResult Viewer::waitForFences(size_t relativeFrameIndex, uint64_t timeout) { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "Viewer waitForFences", COLOR_VIEWER); + VkResult result = VK_SUCCESS; for (auto& task : recordAndSubmitTasks) { @@ -231,6 +250,8 @@ VkResult Viewer::waitForFences(size_t relativeFrameIndex, uint64_t timeout) void Viewer::handleEvents() { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "Viewer handle events", COLOR_UPDATE); + for (auto& vsg_event : _events) { for (auto& handler : _eventHandlers) @@ -242,6 +263,8 @@ void Viewer::handleEvents() void Viewer::compile(ref_ptr hints) { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "Viewer compile", COLOR_COMPILE); + if (recordAndSubmitTasks.empty()) { return; @@ -322,7 +345,11 @@ void Viewer::compile(ref_ptr hints) } } - if (containsPagedLOD && !databasePager) databasePager = DatabasePager::create(); + if (containsPagedLOD && !databasePager) + { + databasePager = DatabasePager::create(); + if (instrumentation) databasePager->assignInstrumentation(instrumentation); + } // create the Vulkan objects for (auto& task : recordAndSubmitTasks) @@ -399,6 +426,8 @@ void Viewer::compile(ref_ptr hints) void Viewer::assignRecordAndSubmitTaskAndPresentation(CommandGraphs in_commandGraphs) { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "Viewer assignRecordAndSubmitTaskAndPresentation", COLOR_VIEWER); + // now remove any commandGraphs associated with window bool needToStartThreading = _threading; if (_threading) stopThreading(); @@ -528,6 +557,9 @@ void Viewer::assignRecordAndSubmitTaskAndPresentation(CommandGraphs in_commandGr recordAndSubmitTask->earlyTransferTask->transferQueue = transferQueue; recordAndSubmitTask->lateTransferTask->transferQueue = transferQueue; + // assign instrumentation + if (instrumentation) recordAndSubmitTask->assignInstrumentation(instrumentation); + auto presentation = vsg::Presentation::create(); presentation->waitSemaphores.emplace_back(renderFinishedSemaphore); presentation->windows = windows; @@ -546,6 +578,9 @@ void Viewer::assignRecordAndSubmitTaskAndPresentation(CommandGraphs in_commandGr recordAndSubmitTask->earlyTransferTask->transferQueue = transferQueue; recordAndSubmitTask->lateTransferTask->transferQueue = transferQueue; + + // assign instrumentation + if (instrumentation) recordAndSubmitTask->assignInstrumentation(instrumentation); } } @@ -554,6 +589,8 @@ void Viewer::assignRecordAndSubmitTaskAndPresentation(CommandGraphs in_commandGr void Viewer::addRecordAndSubmitTaskAndPresentation(CommandGraphs commandGraphs) { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "Viewer addRecordAndSubmitTaskAndPresentation", COLOR_VIEWER); + // collect the existing CommandGraphs CommandGraphs combinedCommandGraphs; for (auto& task : recordAndSubmitTasks) @@ -573,8 +610,6 @@ void Viewer::addRecordAndSubmitTaskAndPresentation(CommandGraphs commandGraphs) void Viewer::setupThreading() { - debug("Viewer::setupThreading() "); - stopThreading(); // check how many valid tasks there are. @@ -604,19 +639,24 @@ void Viewer::setupThreading() if (task->commandGraphs.size() == 1 && !task->earlyTransferTask) { // task only contains a single CommandGraph so keep thread simple - auto run = [](ref_ptr viewer_task, ref_ptr viewer_frameBlock, ref_ptr submissionCompleted) { + auto run = [](ref_ptr viewer_task, ref_ptr viewer_frameBlock, ref_ptr submissionCompleted, const std::string& threadName) { + auto local_instrumentation = shareOrDuplicateForThreadSafety(viewer_task->instrumentation); + if (local_instrumentation) local_instrumentation->setThreadName(threadName); + auto frameStamp = viewer_frameBlock->initial_value; // wait for this frame to be signaled while (viewer_frameBlock->wait_for_change(frameStamp)) { + CPU_INSTRUMENTATION_L1_NC(local_instrumentation, "Viewer run", COLOR_RECORD); + viewer_task->submit(frameStamp); submissionCompleted->arrive_and_drop(); } }; - threads.emplace_back(run, task, _frameBlock, _submissionCompleted); + threads.emplace_back(run, task, _frameBlock, _submissionCompleted, make_string("Viewer run thread")); } else if (!task->commandGraphs.empty()) { @@ -649,12 +689,17 @@ void Viewer::setupThreading() ref_ptr sharedData = SharedData::create(task, _frameBlock, _submissionCompleted, numThreads); - auto run_primary = [](ref_ptr data, ref_ptr commandGraph) { + auto run_primary = [](ref_ptr data, ref_ptr commandGraph, const std::string& threadName) { + auto local_instrumentation = shareOrDuplicateForThreadSafety(data->task->instrumentation); + if (local_instrumentation) local_instrumentation->setThreadName(threadName); + auto frameStamp = data->frameBlock->initial_value; // wait for this frame to be signaled while (data->frameBlock->wait_for_change(frameStamp)) { + CPU_INSTRUMENTATION_L1_NC(local_instrumentation, "Viewer primary", COLOR_RECORD); + // primary thread starts the task data->task->start(); @@ -675,12 +720,17 @@ void Viewer::setupThreading() } }; - auto run_secondary = [](ref_ptr data, ref_ptr commandGraph) { + auto run_secondary = [](ref_ptr data, ref_ptr commandGraph, const std::string& threadName) { + auto local_instrumentation = shareOrDuplicateForThreadSafety(data->task->instrumentation); + if (local_instrumentation) local_instrumentation->setThreadName(threadName); + auto frameStamp = data->frameBlock->initial_value; // wait for this frame to be signaled while (data->frameBlock->wait_for_change(frameStamp)) { + CPU_INSTRUMENTATION_L1_NC(local_instrumentation, "Viewer secondary", COLOR_RECORD); + data->recordStartBarrier->arrive_and_wait(); commandGraph->record(data->recordedCommandBuffers, frameStamp, data->task->databasePager); @@ -689,12 +739,17 @@ void Viewer::setupThreading() } }; - auto run_transfer = [](ref_ptr data, ref_ptr transferTask) { + auto run_transfer = [](ref_ptr data, ref_ptr transferTask, const std::string& threadName) { + auto local_instrumentation = shareOrDuplicateForThreadSafety(data->task->instrumentation); + if (local_instrumentation) local_instrumentation->setThreadName(threadName); + auto frameStamp = data->frameBlock->initial_value; // wait for this frame to be signaled while (data->frameBlock->wait_for_change(frameStamp)) { + CPU_INSTRUMENTATION_L1_NC(local_instrumentation, "Viewer transfer", COLOR_RECORD); + data->recordStartBarrier->arrive_and_wait(); //vsg::info("run_transfer"); @@ -708,14 +763,14 @@ void Viewer::setupThreading() for (uint32_t i = 0; i < task->commandGraphs.size(); ++i) { if (i == 0) - threads.emplace_back(run_primary, sharedData, task->commandGraphs[i]); + threads.emplace_back(run_primary, sharedData, task->commandGraphs[i], make_string("Viewer primary thread")); else - threads.emplace_back(run_secondary, sharedData, task->commandGraphs[i]); + threads.emplace_back(run_secondary, sharedData, task->commandGraphs[i], make_string("Viewer seconary thread ", i)); } if (task->earlyTransferTask) { - threads.emplace_back(run_transfer, sharedData, task->earlyTransferTask); + threads.emplace_back(run_transfer, sharedData, task->earlyTransferTask, make_string("Viewer earlyDynamicData thread")); } } } @@ -723,6 +778,8 @@ void Viewer::setupThreading() void Viewer::stopThreading() { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "Viewer stopThreading", COLOR_VIEWER); + if (!_threading) return; _threading = false; @@ -742,6 +799,8 @@ void Viewer::stopThreading() void Viewer::update() { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "Viewer update", COLOR_UPDATE); + for (auto& task : recordAndSubmitTasks) { if (task->databasePager) @@ -757,6 +816,8 @@ void Viewer::update() void Viewer::recordAndSubmit() { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "Viewer recordAndSubmitTask", COLOR_VIEWER); + // reset connected ExecuteCommands for (auto& recordAndSubmitTask : recordAndSubmitTasks) { @@ -789,13 +850,38 @@ void Viewer::recordAndSubmit() void Viewer::present() { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "Viewer present", COLOR_VIEWER); + for (auto& presentation : presentations) { presentation->present(); } } +void Viewer::assignInstrumentation(ref_ptr in_instrumentation) +{ + bool previous_threading = _threading; + if (_threading) stopThreading(); + + // don't change Instrumentation while devices are still active + Viewer::deviceWaitIdle(); + + instrumentation = in_instrumentation; + + // assign instrumentation after settings up recordAndSubmitTasks, but before compile() to allow compile to initialize the Instrumentation with the approach queue etc. + for (auto& task : recordAndSubmitTasks) + { + task->assignInstrumentation(instrumentation); + } + + if (compileManager) compileManager->assignInstrumentation(instrumentation); + + if (previous_threading) setupThreading(); +} + void vsg::updateViewer(Viewer& viewer, const CompileResult& compileResult) { + CPU_INSTRUMENTATION_L1_NC(viewer.instrumentation, "updateViewer", COLOR_VIEWER); + updateTasks(viewer.recordAndSubmitTasks, viewer.compileManager, compileResult); } diff --git a/src/vsg/core/ConstVisitor.cpp b/src/vsg/core/ConstVisitor.cpp index a5a25c6496..e2f99d1990 100644 --- a/src/vsg/core/ConstVisitor.cpp +++ b/src/vsg/core/ConstVisitor.cpp @@ -609,6 +609,10 @@ void ConstVisitor::apply(const SpotLight& value) { apply(static_cast(value)); } +void ConstVisitor::apply(const InstrumentationNode& value) +{ + apply(static_cast(value)); +} //////////////////////////////////////////////////////////////////////////////// // diff --git a/src/vsg/core/Version.h.in b/src/vsg/core/Version.h.in index 1a341c9448..8dd94169c9 100644 --- a/src/vsg/core/Version.h.in +++ b/src/vsg/core/Version.h.in @@ -30,6 +30,9 @@ extern "C" /// maximum number of logical vkDevice supported #define VSG_MAX_DEVICES @VSG_MAX_DEVICES@ + /// Instrumentation level to built into the VSG ibrary, 0 for off, 1 coarse grained, 2 medium, 3 fine grained. + #define VSG_MAX_INSTRUMENTATION_LEVEL @VSG_MAX_INSTRUMENTATION_LEVEL@ + /// vsg::ShaderCompiler support enabled when 1, disabled when 0 #define VSG_SUPPORTS_ShaderCompiler @VSG_SUPPORTS_ShaderCompiler@ diff --git a/src/vsg/core/Visitor.cpp b/src/vsg/core/Visitor.cpp index 2994805254..a8468a8b7e 100644 --- a/src/vsg/core/Visitor.cpp +++ b/src/vsg/core/Visitor.cpp @@ -609,6 +609,10 @@ void Visitor::apply(SpotLight& value) { apply(static_cast(value)); } +void Visitor::apply(InstrumentationNode& value) +{ + apply(static_cast(value)); +} //////////////////////////////////////////////////////////////////////////////// // diff --git a/src/vsg/io/DatabasePager.cpp b/src/vsg/io/DatabasePager.cpp index d9f991f54a..54ef1c3e3f 100644 --- a/src/vsg/io/DatabasePager.cpp +++ b/src/vsg/io/DatabasePager.cpp @@ -126,6 +126,11 @@ DatabasePager::~DatabasePager() } } +void DatabasePager::assignInstrumentation(ref_ptr in_instrumentation) +{ + instrumentation = in_instrumentation; +} + void DatabasePager::start() { int numReadThreads = 4; @@ -133,14 +138,19 @@ void DatabasePager::start() // // set up read thread(s) // - auto read = [](ref_ptr requestQueue, ref_ptr status, DatabasePager& databasePager) { + auto read = [](ref_ptr requestQueue, ref_ptr status, DatabasePager& databasePager, const std::string& threadName) { debug("Started DatabaseThread read thread"); + auto local_instrumentation = shareOrDuplicateForThreadSafety(databasePager.instrumentation); + if (local_instrumentation) local_instrumentation->setThreadName(threadName); + while (status->active()) { auto plod = requestQueue->take_when_available(); if (plod) { + CPU_INSTRUMENTATION_L1_NC(databasePager.instrumentation, "DatabasePager read", COLOR_PAGER); + uint64_t frameDelta = databasePager.frameCount - plod->frameHighResLastUsed.load(); if (frameDelta > 1 || !compare_exchange(plod->requestStatus, PagedLOD::ReadRequest, PagedLOD::Reading)) { @@ -189,7 +199,7 @@ void DatabasePager::start() for (int i = 0; i < numReadThreads; ++i) { - _readThreads.emplace_back(read, std::ref(_requestQueue), std::ref(_status), std::ref(*this)); + _readThreads.emplace_back(read, std::ref(_requestQueue), std::ref(_status), std::ref(*this), make_string("DatabasePager thread ", i)); } } @@ -233,6 +243,8 @@ void DatabasePager::requestDiscarded(PagedLOD* plod) void DatabasePager::updateSceneGraph(FrameStamp* frameStamp, CompileResult& cr) { + CPU_INSTRUMENTATION_L1(instrumentation); + frameCount.exchange(frameStamp ? frameStamp->frameCount : 0); auto nodes = _toMergeQueue->take_all(cr); diff --git a/src/vsg/io/ObjectFactory.cpp b/src/vsg/io/ObjectFactory.cpp index f0f39cf3f4..73fed680ae 100644 --- a/src/vsg/io/ObjectFactory.cpp +++ b/src/vsg/io/ObjectFactory.cpp @@ -189,6 +189,7 @@ ObjectFactory::ObjectFactory() add(); add(); add(); + add(); // vulkan objects add(); diff --git a/src/vsg/io/Options.cpp b/src/vsg/io/Options.cpp index b121a77749..be74e6ee1a 100644 --- a/src/vsg/io/Options.cpp +++ b/src/vsg/io/Options.cpp @@ -45,7 +45,8 @@ Options::Options(const Options& options) : sceneCoordinateConvention(options.sceneCoordinateConvention), formatCoordinateConventions(options.formatCoordinateConventions), shaderSets(options.shaderSets), - inheritedState(options.inheritedState) + inheritedState(options.inheritedState), + instrumentation(options.instrumentation) { getOrCreateAuxiliary(); // copy any meta data. diff --git a/src/vsg/io/VSG.cpp b/src/vsg/io/VSG.cpp index 5a46a72647..0096498fc0 100644 --- a/src/vsg/io/VSG.cpp +++ b/src/vsg/io/VSG.cpp @@ -93,6 +93,8 @@ void VSG::writeHeader(std::ostream& fout, const FormatInfo& formatInfo) const vsg::ref_ptr VSG::read(const vsg::Path& filename, ref_ptr options) const { + CPU_INSTRUMENTATION_L1_NC(options ? options->instrumentation.get() : nullptr, "VSG read", COLOR_READ); + if (!compatibleExtension(filename, options, ".vsgb", ".vsgt")) return {}; vsg::Path filenameToUse = findFile(filename, options); @@ -123,6 +125,8 @@ vsg::ref_ptr VSG::read(const vsg::Path& filename, ref_ptr VSG::read(std::istream& fin, vsg::ref_ptr options) const { + CPU_INSTRUMENTATION_L1_NC(options ? options->instrumentation.get() : nullptr, "VSG read", COLOR_READ); + if (options && !compatibleExtension(options, ".vsgb", ".vsgt")) return {}; auto [type, version] = readHeader(fin); @@ -144,6 +148,8 @@ vsg::ref_ptr VSG::read(std::istream& fin, vsg::ref_ptr VSG::read(const uint8_t* ptr, size_t size, vsg::ref_ptr options) const { + CPU_INSTRUMENTATION_L1_NC(options ? options->instrumentation.get() : nullptr, "VSG read", COLOR_READ); + if (options && !compatibleExtension(options, ".vsgb", ".vsgt")) return {}; mem_stream fin(ptr, size); @@ -152,6 +158,8 @@ vsg::ref_ptr VSG::read(const uint8_t* ptr, size_t size, vsg::ref_pt bool VSG::write(const vsg::Object* object, const vsg::Path& filename, ref_ptr options) const { + CPU_INSTRUMENTATION_L1_NC(options ? options->instrumentation.get() : nullptr, "VSG write", COLOR_READ); + auto version = vsgGetVersion(); if (options) @@ -192,6 +200,8 @@ bool VSG::write(const vsg::Object* object, const vsg::Path& filename, ref_ptr options) const { + CPU_INSTRUMENTATION_L1_NC(options ? options->instrumentation.get() : nullptr, "VSG write", COLOR_WRITE); + if (options && !compatibleExtension(options, ".vsgb", ".vsgt")) return {}; auto version = vsgGetVersion(); diff --git a/src/vsg/io/glsl.cpp b/src/vsg/io/glsl.cpp index 3802c3ed5a..c12eeb3e27 100644 --- a/src/vsg/io/glsl.cpp +++ b/src/vsg/io/glsl.cpp @@ -48,6 +48,8 @@ bool glsl::extensionSupported(const Path& ext) ref_ptr glsl::createShader(const Path& found_filename, std::string& source, VkShaderStageFlagBits stageFlagBits, ref_ptr options) const { + CPU_INSTRUMENTATION_L2_NC(options ? options->instrumentation.get() : nullptr, "glsl createShader", COLOR_READ); + // handle any #includes in the source if (source.find("include") != std::string::npos) { @@ -66,6 +68,8 @@ ref_ptr glsl::createShader(const Path& found_filename, std::string& sour ref_ptr glsl::read(const Path& filename, ref_ptr options) const { + CPU_INSTRUMENTATION_L1_NC(options ? options->instrumentation.get() : nullptr, "glsl read", COLOR_READ); + auto stage_itr = (options && options->extensionHint) ? s_extensionToStage.find(options->extensionHint) : s_extensionToStage.end(); if (stage_itr == s_extensionToStage.end()) stage_itr = s_extensionToStage.find(lowerCaseFileExtension(filename)); if (stage_itr == s_extensionToStage.end()) return {}; @@ -88,6 +92,8 @@ ref_ptr glsl::read(const Path& filename, ref_ptr options) ref_ptr glsl::read(std::istream& fin, ref_ptr options) const { + CPU_INSTRUMENTATION_L1_NC(options ? options->instrumentation.get() : nullptr, "glsl read", COLOR_READ); + auto stage_itr = (options && options->extensionHint) ? s_extensionToStage.find(options->extensionHint) : s_extensionToStage.end(); if (stage_itr == s_extensionToStage.end()) return {}; @@ -104,6 +110,8 @@ ref_ptr glsl::read(std::istream& fin, ref_ptr option ref_ptr glsl::read(const uint8_t* ptr, size_t size, ref_ptr options) const { + CPU_INSTRUMENTATION_L1_NC(options ? options->instrumentation.get() : nullptr, "glsl read", COLOR_READ); + auto stage_itr = (options && options->extensionHint) ? s_extensionToStage.find(options->extensionHint) : s_extensionToStage.end(); if (stage_itr == s_extensionToStage.end()) return {}; @@ -114,6 +122,8 @@ ref_ptr glsl::read(const uint8_t* ptr, size_t size, ref_ptr options) const { + CPU_INSTRUMENTATION_L1_NC(options ? options->instrumentation.get() : nullptr, "glsl write", COLOR_READ); + auto stage_itr = (options && options->extensionHint) ? s_extensionToStage.find(options->extensionHint) : s_extensionToStage.end(); if (stage_itr == s_extensionToStage.end()) stage_itr = s_extensionToStage.find(lowerCaseFileExtension(filename)); if (stage_itr == s_extensionToStage.end()) return false; diff --git a/src/vsg/io/read.cpp b/src/vsg/io/read.cpp index d4424327c5..4c469c6e42 100644 --- a/src/vsg/io/read.cpp +++ b/src/vsg/io/read.cpp @@ -23,6 +23,8 @@ using namespace vsg; ref_ptr vsg::read(const Path& filename, ref_ptr options) { + CPU_INSTRUMENTATION_L1_NC(options ? options->instrumentation.get() : nullptr, "read", COLOR_READ); + auto read_file = [&]() -> ref_ptr { if (options && !options->readerWriters.empty()) { @@ -81,6 +83,8 @@ ref_ptr vsg::read(const Path& filename, ref_ptr options) PathObjects vsg::read(const Paths& filenames, ref_ptr options) { + CPU_INSTRUMENTATION_L1_NC(options ? options->instrumentation.get() : nullptr, "read", COLOR_READ); + ref_ptr operationThreads; if (options) operationThreads = options->operationThreads; @@ -150,6 +154,8 @@ PathObjects vsg::read(const Paths& filenames, ref_ptr options) ref_ptr vsg::read(std::istream& fin, ref_ptr options) { + CPU_INSTRUMENTATION_L1_NC(options ? options->instrumentation.get() : nullptr, "read", COLOR_READ); + if (options && !options->readerWriters.empty()) { for (auto& readerWriter : options->readerWriters) @@ -164,6 +170,8 @@ ref_ptr vsg::read(std::istream& fin, ref_ptr options) ref_ptr vsg::read(const uint8_t* ptr, size_t size, ref_ptr options) { + CPU_INSTRUMENTATION_L1_NC(options ? options->instrumentation.get() : nullptr, "read", COLOR_READ); + if (options && !options->readerWriters.empty()) { for (auto& readerWriter : options->readerWriters) diff --git a/src/vsg/io/spirv.cpp b/src/vsg/io/spirv.cpp index e1a50f7312..249b80027c 100644 --- a/src/vsg/io/spirv.cpp +++ b/src/vsg/io/spirv.cpp @@ -45,6 +45,8 @@ spirv::spirv() vsg::ref_ptr spirv::read(const vsg::Path& filename, vsg::ref_ptr options) const { + CPU_INSTRUMENTATION_L1_NC(options ? options->instrumentation.get() : nullptr, "spirv read", COLOR_READ); + if (!compatibleExtension(filename, options, ".spv")) return {}; vsg::Path found_filename = vsg::findFile(filename, options); @@ -57,6 +59,8 @@ vsg::ref_ptr spirv::read(const vsg::Path& filename, vsg::ref_ptr spirv::read(std::istream& fin, ref_ptr options) const { + CPU_INSTRUMENTATION_L1_NC(options ? options->instrumentation.get() : nullptr, "spirv read", COLOR_READ); + if (!compatibleExtension(options, ".spv")) return {}; fin.seekg(0, fin.end); @@ -77,6 +81,8 @@ ref_ptr spirv::read(std::istream& fin, ref_ptr optio ref_ptr spirv::read(const uint8_t* ptr, size_t size, ref_ptr options) const { + CPU_INSTRUMENTATION_L1(options ? options->instrumentation.get() : nullptr); + if (!compatibleExtension(options, ".spv")) return {}; using value_type = vsg::ShaderModule::SPIRV::value_type; @@ -91,6 +97,8 @@ ref_ptr spirv::read(const uint8_t* ptr, size_t size, ref_ptr options) const { + CPU_INSTRUMENTATION_L1_NC(options ? options->instrumentation.get() : nullptr, "spirv write", COLOR_WRITE); + if (!compatibleExtension(filename, options, ".spv")) return false; const vsg::ShaderStage* ss = dynamic_cast(object); diff --git a/src/vsg/io/tile.cpp b/src/vsg/io/tile.cpp index 83d983c3d0..02663237bc 100644 --- a/src/vsg/io/tile.cpp +++ b/src/vsg/io/tile.cpp @@ -23,6 +23,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include #include #include #include @@ -117,6 +118,8 @@ vsg::Path tile::getTilePath(const vsg::Path& src, uint32_t x, uint32_t y, uint32 vsg::ref_ptr tile::read(const vsg::Path& filename, vsg::ref_ptr options) const { + CPU_INSTRUMENTATION_L1_NC(options ? options->instrumentation.get() : nullptr, "tile read", COLOR_READ); + auto extension = vsg::lowerCaseFileExtension(filename); if (extension != ".tile") return {}; @@ -140,6 +143,8 @@ vsg::ref_ptr tile::read(const vsg::Path& filename, vsg::ref_ptr tile::read_root(vsg::ref_ptr options) const { + CPU_INSTRUMENTATION_L2_NC(options ? options->instrumentation.get() : nullptr, "tile read_root", COLOR_READ); + auto group = createRoot(); uint32_t lod = 0; @@ -199,8 +204,9 @@ vsg::ref_ptr tile::read_root(vsg::ref_ptr optio vsg::ref_ptr tile::read_subtile(uint32_t x, uint32_t y, uint32_t lod, vsg::ref_ptr options) const { - // need to load subtile x y lod + CPU_INSTRUMENTATION_L2_NC(options ? options->instrumentation.get() : nullptr, "tile read_subtile", COLOR_READ); + // need to load subtile x y lod vsg::time_point start_read = vsg::clock::now(); auto group = vsg::Group::create(); @@ -294,6 +300,8 @@ vsg::ref_ptr tile::read_subtile(uint32_t x, uint32_t y, uint32_t lo void tile::init(vsg::ref_ptr options) { + CPU_INSTRUMENTATION_L2_NC(options ? options->instrumentation.get() : nullptr, "tile init", COLOR_READ); + if (settings->shaderSet) { _shaderSet = settings->shaderSet; @@ -469,13 +477,13 @@ vsg::ref_ptr tile::createECEFTile(const vsg::dbox& tile_extents, vsg: } // setup geometry - auto drawCommands = vsg::Commands::create(); - drawCommands->addChild(vsg::BindVertexBuffers::create(0, vsg::DataList{vertices, normals, texcoords, colors})); - drawCommands->addChild(vsg::BindIndexBuffer::create(indices)); - drawCommands->addChild(vsg::DrawIndexed::create(static_cast(indices->size()), 1, 0, 0, 0)); + auto vid = vsg::VertexIndexDraw::create(); + vid->assignArrays(vsg::DataList{vertices, normals, texcoords, colors}); + vid->assignIndices(indices); + vid->indexCount = static_cast(indices->size()); + vid->instanceCount = 1; - // add drawCommands to transform - transform->addChild(drawCommands); + transform->addChild(vid); return scenegraph; } diff --git a/src/vsg/io/write.cpp b/src/vsg/io/write.cpp index 9e8bc552a2..52ca16bee3 100644 --- a/src/vsg/io/write.cpp +++ b/src/vsg/io/write.cpp @@ -20,6 +20,8 @@ using namespace vsg; bool vsg::write(ref_ptr object, const Path& filename, ref_ptr options) { + CPU_INSTRUMENTATION_L1_NC(options ? options->instrumentation.get() : nullptr, "write", COLOR_WRITE); + bool fileWritten = false; if (options) { diff --git a/src/vsg/nodes/InstrumentationNode.cpp b/src/vsg/nodes/InstrumentationNode.cpp new file mode 100644 index 0000000000..cfd24d6d3c --- /dev/null +++ b/src/vsg/nodes/InstrumentationNode.cpp @@ -0,0 +1,120 @@ +/* + +Copyright(c) 2024 Robert Osfield + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + */ + +#include +#include +#include +#include + +using namespace vsg; + +InstrumentationNode::InstrumentationNode(): + _level(1), + _color(255, 255, 255, 255), + _name("InstrumentationNode"), + _sl_Visitor{_name.c_str(), "InstrumentationNode::traverse(Visitor& rt)", __FILE__, 42, _color, _level}, + _sl_ConstVisitor{_name.c_str(), "InstrumentationNode::traverse(Visitor& rt)", __FILE__, 48, _color, _level}, + _sl_RecordTraversal{_name.c_str(), "InstrumentationNode::traverse(Visitor& rt)", __FILE__, 54, _color, _level} +{ +} + +InstrumentationNode::InstrumentationNode(ref_ptr in_child) : + InstrumentationNode() +{ + child = in_child; +} + +InstrumentationNode::~InstrumentationNode() +{ +} + +void InstrumentationNode::traverse(Visitor& visitor) +{ + CpuInstrumentation cpuInst(visitor.getInstrumentation(), &_sl_Visitor, child.get()); + child->accept(visitor); +} + +void InstrumentationNode::traverse(ConstVisitor& visitor) const +{ + CpuInstrumentation cpuInst(visitor.getInstrumentation(), &_sl_ConstVisitor, child.get()); + child->accept(visitor); +} + +void InstrumentationNode::traverse(RecordTraversal& rt) const +{ + GpuInstrumentation cpuInst(rt.instrumentation, &_sl_RecordTraversal, *rt.getCommandBuffer(), child.get()); + child->accept(rt); +} + +void InstrumentationNode::setColor(uint_color color) +{ + _color = color; + _sl_Visitor.color = _color; + _sl_ConstVisitor.color = _color; + _sl_RecordTraversal.color = _color; +} + +void InstrumentationNode::setName(const std::string& name) +{ + _name = name; + if (_name.empty()) + { + _sl_Visitor.name = nullptr; + _sl_ConstVisitor.name = nullptr; + _sl_RecordTraversal.name = nullptr; + } + else + { + _sl_Visitor.name = _name.c_str(); + _sl_ConstVisitor.name = _name.c_str(); + _sl_RecordTraversal.name = _name.c_str(); + } +} + +void InstrumentationNode::setLevel(uint32_t level) +{ + _level = level; + _sl_Visitor.level = _level; + _sl_ConstVisitor.level = _level; + _sl_RecordTraversal.level = _level; +} + +void InstrumentationNode::read(Input& input) +{ + Node::read(input); + + uint32_t level = 0; + uint_color color; + std::string name; + + input.read("Level", level); + input.read("Color", color); + input.read("Name", name); + + setLevel(level); + setColor(color); + setName(name); + + input.read("child", child); + +} + +void InstrumentationNode::write(Output& output) const +{ + Node::write(output); + + output.write("Level", _level); + output.write("Color", _color); + output.write("Name", _name); + + output.write("child", child); +} diff --git a/src/vsg/state/ViewDependentState.cpp b/src/vsg/state/ViewDependentState.cpp index 7d1e280075..7a83a32624 100644 --- a/src/vsg/state/ViewDependentState.cpp +++ b/src/vsg/state/ViewDependentState.cpp @@ -375,6 +375,8 @@ void ViewDependentState::update(ResourceRequirements& requirements) void ViewDependentState::compile(Context& context) { + CPU_INSTRUMENTATION_L1_NC(context.instrumentation, "ViewDependentState compile", COLOR_COMPILE); + descriptorSet->compile(context); if ((view->features & RECORD_SHADOW_MAPS) != 0 && preRenderCommandGraph && !preRenderCommandGraph->device) @@ -484,6 +486,9 @@ void ViewDependentState::clear() void ViewDependentState::traverse(RecordTraversal& rt) const { + //GPU_INSTRUMENTATION_L1_NC(rt.instrumentation, *rt.getCommandBuffer(), "ViewDependentState", COLOR_RECORD_L1); + CPU_INSTRUMENTATION_L1_NC(rt.instrumentation, "ViewDependentState", COLOR_RECORD_L1); + if ((view->features & RECORD_SHADOW_MAPS) == 0) return; // useful reference : https://learn.microsoft.com/en-us/windows/win32/dxtecharts/cascaded-shadow-maps @@ -702,6 +707,11 @@ void ViewDependentState::traverse(RecordTraversal& rt) const if (requiresPerRenderShadowMaps && preRenderCommandGraph) { + if (rt.instrumentation && !preRenderCommandGraph->instrumentation) + { + preRenderCommandGraph->instrumentation = shareOrDuplicateForThreadSafety(rt.instrumentation); + } + // info("ViewDependentState::traverse(RecordTraversal&) doing pre render command graph. shadowMapIndex = ", shadowMapIndex); preRenderCommandGraph->accept(rt); } diff --git a/src/vsg/utils/GpuAnnotation.cpp b/src/vsg/utils/GpuAnnotation.cpp new file mode 100644 index 0000000000..e73c1f7f59 --- /dev/null +++ b/src/vsg/utils/GpuAnnotation.cpp @@ -0,0 +1,62 @@ +/* + +Copyright(c) 2023 Robert Osfield + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + */ + +#include +#include +#include + +using namespace vsg; + +GpuAnnotation::GpuAnnotation() +{ +} + +GpuAnnotation::~GpuAnnotation() +{ +} + +void GpuAnnotation::enterCommandBuffer(const SourceLocation* sl, uint64_t& reference, CommandBuffer& commandBuffer) const +{ + enter(sl, reference, commandBuffer, nullptr); +} + +void GpuAnnotation::leaveCommandBuffer(const SourceLocation* sl, uint64_t& reference, CommandBuffer& commandBuffer) const +{ + leave(sl, reference, commandBuffer, nullptr); +} + +void GpuAnnotation::enter(const SourceLocation* sl, uint64_t&, vsg::CommandBuffer& commandBuffer, const Object* object) const +{ + auto extensions = commandBuffer.getDevice()->getInstance()->getExtensions(); + + VkDebugUtilsLabelEXT markerInfo = {}; + markerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; + if (sl->name && labelType == SourceLocation_name) + markerInfo.pLabelName = sl->name; + else if (labelType == Object_className && object) + markerInfo.pLabelName = object->className(); + else + markerInfo.pLabelName = sl->function; + + markerInfo.color[0] = static_cast(sl->color.r) / 255.0f; + markerInfo.color[1] = static_cast(sl->color.g) / 255.0f; + markerInfo.color[2] = static_cast(sl->color.b) / 255.0f; + markerInfo.color[3] = static_cast(sl->color.a) / 255.0f; + + extensions->vkCmdBeginDebugUtilsLabelEXT(commandBuffer, &markerInfo); +} + +void GpuAnnotation::leave(const SourceLocation*, uint64_t&, vsg::CommandBuffer& commandBuffer, const Object*) const +{ + auto extensions = commandBuffer.getDevice()->getInstance()->getExtensions(); + extensions->vkCmdEndDebugUtilsLabelEXT(commandBuffer); +} diff --git a/src/vsg/utils/Instrumentation.cpp b/src/vsg/utils/Instrumentation.cpp new file mode 100644 index 0000000000..143ecda4fd --- /dev/null +++ b/src/vsg/utils/Instrumentation.cpp @@ -0,0 +1,35 @@ +/* + +Copyright(c) 2023 Robert Osfield + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + */ + +#include +#include +#include +#include + +using namespace vsg; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Instrumentation base class +// +Instrumentation::Instrumentation() +{ +} + +Instrumentation::~Instrumentation() +{ +} + +ref_ptr vsg::shareOrDuplicateForThreadSafety(ref_ptr instrumentation) +{ + return instrumentation ? instrumentation->shareOrDuplicateForThreadSafety() : instrumentation; +} diff --git a/src/vsg/vk/CommandPool.cpp b/src/vsg/vk/CommandPool.cpp index fc5520bbfe..1f8e9f7673 100644 --- a/src/vsg/vk/CommandPool.cpp +++ b/src/vsg/vk/CommandPool.cpp @@ -17,7 +17,9 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI using namespace vsg; -CommandPool::CommandPool(Device* device, uint32_t queueFamilyIndex, VkCommandPoolCreateFlags flags) : +CommandPool::CommandPool(Device* device, uint32_t in_queueFamilyIndex, VkCommandPoolCreateFlags in_flags) : + queueFamilyIndex(in_queueFamilyIndex), + flags(in_flags), _device(device) { VkCommandPoolCreateInfo poolInfo = {}; diff --git a/src/vsg/vk/Context.cpp b/src/vsg/vk/Context.cpp index 2de0b6cd9f..b07a517ba2 100644 --- a/src/vsg/vk/Context.cpp +++ b/src/vsg/vk/Context.cpp @@ -157,6 +157,8 @@ ShaderCompiler* Context::getOrCreateShaderCompiler() void Context::getDescriptorPoolSizesToUse(uint32_t& maxSets, DescriptorPoolSizes& descriptorPoolSizes) { + CPU_INSTRUMENTATION_L2_NC(instrumentation, "Context getDescriptorPoolSizesToUse", COLOR_COMPILE) + if (minimum_maxSets > maxSets) { maxSets = minimum_maxSets; @@ -183,6 +185,8 @@ void Context::getDescriptorPoolSizesToUse(uint32_t& maxSets, DescriptorPoolSizes ref_ptr Context::allocateDescriptorSet(DescriptorSetLayout* descriptorSetLayout) { + CPU_INSTRUMENTATION_L2_NC(instrumentation, "Context allocateDescriptorSet", COLOR_COMPILE) + for (auto itr = descriptorPools.rbegin(); itr != descriptorPools.rend(); ++itr) { auto dsi = (*itr)->allocateDescriptorSet(descriptorSetLayout); @@ -204,6 +208,8 @@ ref_ptr Context::allocateDescriptorSet(Descriptor void Context::reserve(const ResourceRequirements& requirements) { + CPU_INSTRUMENTATION_L2_NC(instrumentation, "Context reserve", COLOR_COMPILE) + if (requirements.maxSlot > resourceRequirements.maxSlot) { resourceRequirements.maxSlot = requirements.maxSlot; @@ -262,6 +268,8 @@ void Context::reserve(const ResourceRequirements& requirements) void Context::copy(ref_ptr data, ref_ptr dest) { + CPU_INSTRUMENTATION_L2_NC(instrumentation, "Context copy", COLOR_COMPILE) + if (!copyImageCmd) { copyImageCmd = CopyAndReleaseImage::create(stagingMemoryBufferPools); @@ -273,6 +281,8 @@ void Context::copy(ref_ptr data, ref_ptr dest) void Context::copy(ref_ptr data, ref_ptr dest, uint32_t numMipMapLevels) { + CPU_INSTRUMENTATION_L2_NC(instrumentation, "Context copy", COLOR_COMPILE) + if (!copyImageCmd) { copyImageCmd = CopyAndReleaseImage::create(stagingMemoryBufferPools); @@ -284,6 +294,8 @@ void Context::copy(ref_ptr data, ref_ptr dest, uint32_t numMipM void Context::copy(ref_ptr src, ref_ptr dest) { + CPU_INSTRUMENTATION_L2_NC(instrumentation, "Context copy", COLOR_COMPILE) + if (!copyBufferCmd) { copyBufferCmd = CopyAndReleaseBuffer::create(); @@ -295,6 +307,8 @@ void Context::copy(ref_ptr src, ref_ptr dest) bool Context::record() { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "Context record", COLOR_COMPILE) + if (commands.empty() && buildAccelerationStructureCommands.empty()) return false; //auto before_compile = std::chrono::steady_clock::now(); @@ -316,22 +330,24 @@ bool Context::record() vkBeginCommandBuffer(*commandBuffer, &beginInfo); - // issue commands of interest { - for (auto& command : commands) command->record(*commandBuffer); - } + COMMAND_BUFFER_INSTRUMENTATION(instrumentation, *commandBuffer, "Context record", COLOR_COMPILE) - // create scratch buffer and issue build acceleration structure commands - ref_ptr scratchBuffer; - ref_ptr scratchBufferMemory; - if (scratchBufferSize > 0) - { - scratchBuffer = vsg::createBufferAndMemory(device, scratchBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_SHARING_MODE_EXCLUSIVE, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + // issue commands of interest + { + for (auto& command : commands) command->record(*commandBuffer); + } - for (auto& command : buildAccelerationStructureCommands) + // create scratch buffer and issue build acceleration structure commands + if (scratchBufferSize > 0) { - command->setScratchBuffer(scratchBuffer); - command->record(*commandBuffer); + ref_ptr scratchBuffer = vsg::createBufferAndMemory(device, scratchBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_SHARING_MODE_EXCLUSIVE, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + for (auto& command : buildAccelerationStructureCommands) + { + command->setScratchBuffer(scratchBuffer); + command->record(*commandBuffer); + } } } @@ -362,6 +378,8 @@ bool Context::record() void Context::waitForCompletion() { + CPU_INSTRUMENTATION_L1_NC(instrumentation, "Context waitForCompletion", COLOR_COMPILE) + if (!commandBuffer || !fence) { return; diff --git a/src/vsg/vk/Device.cpp b/src/vsg/vk/Device.cpp index 570c6571cb..85ae23ab81 100644 --- a/src/vsg/vk/Device.cpp +++ b/src/vsg/vk/Device.cpp @@ -52,6 +52,7 @@ static void releaseDeviceID(uint32_t deviceID) Device::Device(PhysicalDevice* physicalDevice, const QueueSettings& queueSettings, Names layers, Names deviceExtensions, const DeviceFeatures* deviceFeatures, AllocationCallbacks* allocator) : deviceID(getUniqueDeviceID()), + enabledExtensions(deviceExtensions), _instance(physicalDevice->getInstance()), _physicalDevice(physicalDevice), _allocator(allocator) @@ -197,3 +198,9 @@ bool Device::supportsApiVersion(uint32_t version) const { return getInstance()->apiVersion >= version && _physicalDevice->getProperties().apiVersion >= version; } + +bool Device::supportsDeviceExtension(const char* extensionName) const +{ + auto compare = [&](const char* rhs) { return strcmp(extensionName, rhs) == 0; }; + return (std::find_if(enabledExtensions.begin(), enabledExtensions.end(), compare) != enabledExtensions.end()); +} diff --git a/src/vsg/vk/DeviceExtensions.cpp b/src/vsg/vk/DeviceExtensions.cpp index 00ce8a0042..54abf3ce82 100644 --- a/src/vsg/vk/DeviceExtensions.cpp +++ b/src/vsg/vk/DeviceExtensions.cpp @@ -19,26 +19,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI using namespace vsg; -bool vsg::isExtensionSupported(const char* extensionName) -{ - auto extProps = enumerateInstanceExtensionProperties(); - for (const auto& prop : extProps) - { - if (strncmp(prop.extensionName, extensionName, VK_MAX_EXTENSION_NAME_SIZE) == 0) return true; - } - return false; -} - -bool vsg::isExtensionListSupported(const Names& extensionList) -{ - auto extProps = enumerateInstanceExtensionProperties(); - for (const auto& ext : extensionList) - { - auto compare = [&](const VkExtensionProperties& rhs) { return strcmp(ext, rhs.extensionName) == 0; }; - if (std::find_if(extProps.begin(), extProps.end(), compare) == extProps.end()) return false; - } - return true; -} DeviceExtensions::DeviceExtensions(Device* device) { diff --git a/src/vsg/vk/Instance.cpp b/src/vsg/vk/Instance.cpp index 730b27ea54..c26e28b3b4 100644 --- a/src/vsg/vk/Instance.cpp +++ b/src/vsg/vk/Instance.cpp @@ -23,17 +23,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI using namespace vsg; -InstanceLayerProperties vsg::enumerateInstanceLayerProperties() -{ - uint32_t layerCount; - vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - - std::vector availableLayers(layerCount); - vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); - return availableLayers; -} - -InstanceExtensionProperties vsg::enumerateInstanceExtensionProperties(const char* pLayerName) +ExtensionProperties vsg::enumerateInstanceExtensionProperties(const char* pLayerName) { uint32_t extCount = 0; VkResult result = vkEnumerateInstanceExtensionProperties(pLayerName, &extCount, nullptr); @@ -43,7 +33,7 @@ InstanceExtensionProperties vsg::enumerateInstanceExtensionProperties(const char return {}; } - InstanceExtensionProperties extensionProperties(extCount); + ExtensionProperties extensionProperties(extCount); result = vkEnumerateInstanceExtensionProperties(pLayerName, &extCount, extensionProperties.data()); if (result != VK_SUCCESS) { @@ -53,6 +43,23 @@ InstanceExtensionProperties vsg::enumerateInstanceExtensionProperties(const char return extensionProperties; } +bool vsg::isExtensionSupported(const char* extensionName, const char* pLayerName) +{ + auto extProps = enumerateInstanceExtensionProperties(pLayerName); + auto compare = [&](const VkExtensionProperties& rhs) { return strcmp(extensionName, rhs.extensionName) == 0; }; + return std::find_if(extProps.begin(), extProps.end(), compare) != extProps.end(); +} + +InstanceLayerProperties vsg::enumerateInstanceLayerProperties() +{ + uint32_t layerCount; + vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + + std::vector availableLayers(layerCount); + vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); + return availableLayers; +} + Names vsg::validateInstancelayerNames(const Names& names) { if (names.empty()) return names; diff --git a/src/vsg/vk/InstanceExtensions.cpp b/src/vsg/vk/InstanceExtensions.cpp index 14f85b8fe6..2dc586f9bf 100644 --- a/src/vsg/vk/InstanceExtensions.cpp +++ b/src/vsg/vk/InstanceExtensions.cpp @@ -37,4 +37,8 @@ InstanceExtensions::InstanceExtensions(Instance* instance) instance->getProcAddr(vkCreateDebugUtilsMessengerEXT, "vkCreateDebugUtilsMessengerEXT"); instance->getProcAddr(vkDestroyDebugUtilsMessengerEXT, "vkDestroyDebugUtilsMessengerEXT"); instance->getProcAddr(vkSubmitDebugUtilsMessageEXT, "vkSubmitDebugUtilsMessageEXT"); + + // VK_EXT_calibrated_timestamps + instance->getProcAddr(vkGetPhysicalDeviceCalibrateableTimeDomainsEXT, "vkGetPhysicalDeviceCalibrateableTimeDomainsKHR", "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT"); + instance->getProcAddr(vkGetCalibratedTimestampsEXT, "vkGetCalibratedTimestampsKHR", "vkGetCalibratedTimestampsEXT"); }