diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f330b6..7f56d1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ option(GFX_BUILD_VULKAN "Build the vulkan backend" ON) option(GFX_ENABLE_IMGUI "Build with imgui capabilities" OFF) option(GFX_ENABLE_GLFW "Build with glfw capabilities" OFF) option(GFX_BUILD_EXAMPLES "Build Graphics examples executables" OFF) +option(GFX_BUILD_TRACY "Build the tracy client" OFF) option(GFX_INSTALL "Enable the install command" ON) if(GFX_BUILD_EXAMPLES) @@ -115,6 +116,21 @@ if (GFX_BUILD_VULKAN) set_target_properties(VulkanMemoryAllocator PROPERTIES FOLDER "dependencies") endif() +if (GFX_BUILD_TRACY) + FetchContent_Declare(tracy + GIT_REPOSITORY https://github.com/wolfpld/tracy.git + GIT_TAG v0.13.0 + GIT_SHALLOW 1 + GIT_PROGRESS TRUE + ) + set(TRACY_LTO ON) + FetchContent_MakeAvailable(tracy) + if(NOT MSVC) + target_compile_options(TracyClient PRIVATE -Wno-deprecated-declarations) + endif() + set_target_properties(TracyClient PROPERTIES FOLDER "dependencies") +endif() + add_library(Graphics ${GFX_LIB_TYPE}) # Use GFX_LIB_TYPE to override BUILD_SHARED_LIBS for `Graphics` target_compile_features(Graphics PUBLIC cxx_std_23) @@ -178,6 +194,10 @@ if (GFX_ENABLE_GLFW) target_compile_definitions(Graphics PUBLIC "GFX_GLFW_ENABLED") endif() +if (GFX_BUILD_TRACY) + target_compile_definitions(Graphics PUBLIC "GFX_BUILD_TRACY") +endif() + if (GFX_BUILD_METAL) target_link_libraries(Graphics PRIVATE ${METAL_FRAMEWORK} ${QUARTZ_CORE_FRAMEWORK} ${FOUNDATION_FRAMEWORK}) endif() @@ -190,6 +210,10 @@ if (GFX_ENABLE_GLFW OR GFX_ENABLE_IMGUI) target_link_libraries(Graphics PRIVATE dlLoad) endif() +if (GFX_BUILD_TRACY) + target_link_libraries(Graphics PUBLIC TracyClient) +endif() + add_subdirectory("tools") if(GFX_BUILD_EXAMPLES) diff --git a/examples/imgui_usage/imgui_usage.cpp b/examples/imgui_usage/imgui_usage.cpp index cd911de..3a48db4 100644 --- a/examples/imgui_usage/imgui_usage.cpp +++ b/examples/imgui_usage/imgui_usage.cpp @@ -27,8 +27,8 @@ #if __XCODE__ #include #endif -#if (defined(__GNUC__) || defined(__clang__)) - #define GFX_EXPORT __attribute__((visibility("default"))) +#if defined(__GNUC__) + #define GFX_EXPORT __attribute__((used, visibility("default"))) #elif defined(_MSC_VER) #define GFX_EXPORT __declspec(dllexport) #else diff --git a/examples/mc_cube/mc_cube.cpp b/examples/mc_cube/mc_cube.cpp index 3b9a81b..de97403 100644 --- a/examples/mc_cube/mc_cube.cpp +++ b/examples/mc_cube/mc_cube.cpp @@ -44,8 +44,8 @@ #include #endif -#if (defined(__GNUC__) || defined(__clang__)) - #define GFX_EXPORT __attribute__((visibility("default"))) +#if defined(__GNUC__) + #define GFX_EXPORT __attribute__((used, visibility("default"))) #elif defined(_MSC_VER) #define GFX_EXPORT __declspec(dllexport) #else diff --git a/examples/scop/AssetLoader.cpp b/examples/scop/AssetLoader.cpp index a36a0c4..2bf2acb 100644 --- a/examples/scop/AssetLoader.cpp +++ b/examples/scop/AssetLoader.cpp @@ -17,21 +17,27 @@ #include #if !defined (SCOP_MANDATORY) -#include -#include -#include -#include -#include -#include + #include + #include + #include + #include + #include + #include #else -#include "math/math.hpp" -#include "ObjParser/ObjParser.hpp" -#ifndef SCOP_MATH_GLM_ALIAS_DEFINED -#define SCOP_MATH_GLM_ALIAS_DEFINED -namespace glm = scop::math; -#endif + #include "math/math.hpp" + #include "ObjParser/ObjParser.hpp" + #ifndef SCOP_MATH_GLM_ALIAS_DEFINED + #define SCOP_MATH_GLM_ALIAS_DEFINED + namespace glm = scop::math; + #endif #endif #include +#if defined (GFX_BUILD_TRACY) + #include +#else + #define ZoneScoped + #define ZoneScopedN(x) +#endif #include #include // IWYU pragma: keep @@ -374,6 +380,7 @@ Mesh AssetLoader::builtinCube(const std::shared_ptr& material) #if !defined (SCOP_MANDATORY) Mesh AssetLoader::loadMesh(const std::filesystem::path& path, std::optional> overrideMaterial) { + ZoneScoped; assert(std::filesystem::is_regular_file(path)); Assimp::Importer importer; @@ -392,7 +399,11 @@ Mesh AssetLoader::loadMesh(const std::filesystem::path& path, std::optionalmNumMaterials, *overrideMaterial); } else { - std::unique_ptr parameterBlockPool = m_device->newParameterBlockPool({ .maxUniformBuffers = 130, .maxTextures = 380, .maxSamplers = 130 }); + std::unique_ptr parameterBlockPool = m_device->newParameterBlockPool({ + .maxUniformBuffers = 500, + .maxTextures = 500, + .maxSamplers = 500 + }); assert(parameterBlockPool); std::map> textureCache; @@ -416,6 +427,8 @@ Mesh AssetLoader::loadMesh(const std::filesystem::path& path, std::optionalmMaterials, scene->mNumMaterials) | std::views::transform([&](aiMaterial* aiMaterial) -> std::shared_ptr { + ZoneScopedN("makeMaterial"); + auto material = std::make_shared(*m_device); aiColor4D diffuseColor{}; @@ -612,6 +625,8 @@ using UniqueStbiUc = std::unique_ptr; #if !defined (SCOP_MANDATORY) std::shared_ptr AssetLoader::loadEmbeddedTexture(const aiTexture* aiTex, gfx::CommandBuffer& commandBuffer) { + ZoneScoped; + int width = 0; int height = 0; UniqueStbiUc bytes(nullptr, stbi_image_free); @@ -661,6 +676,8 @@ std::shared_ptr AssetLoader::loadEmbeddedTexture(const aiTexture* std::shared_ptr AssetLoader::loadTexture(const std::filesystem::path& path, gfx::CommandBuffer& commandBuffer) { + ZoneScoped; + int width = 0; int height = 0; UniqueStbiUc bytes = UniqueStbiUc(stbi_load(path.string().c_str(), &width, &height, nullptr, STBI_rgb_alpha), stbi_image_free); @@ -693,6 +710,8 @@ std::shared_ptr AssetLoader::loadTexture(const std::filesystem::pa std::shared_ptr AssetLoader::loadCubeTexture(const std::filesystem::path& right, const std::filesystem::path& left, const std::filesystem::path& top, const std::filesystem::path& bottom, const std::filesystem::path& front, const std::filesystem::path& back, gfx::CommandBuffer& commandBuffer) { + ZoneScoped; + int width = 0; int height = 0; std::map bytes; @@ -748,6 +767,8 @@ std::shared_ptr AssetLoader::loadCubeTexture(const std::filesystem std::shared_ptr AssetLoader::getSolidColorTexture(const glm::vec4& color, gfx::CommandBuffer& commandBuffer) { + ZoneScoped; + std::scoped_lock lock(m_solidColorTextureCacheMtx); auto it =std::ranges::find_if(m_solidColorTextureCache, [&](const auto& e){ return e.first == color; }); diff --git a/examples/scop/AssetLoader.hpp b/examples/scop/AssetLoader.hpp index b43ab69..a55733e 100644 --- a/examples/scop/AssetLoader.hpp +++ b/examples/scop/AssetLoader.hpp @@ -19,13 +19,13 @@ #include #if !defined (SCOP_MANDATORY) -#include + #include #else -#include "math/math.hpp" -#ifndef SCOP_MATH_GLM_ALIAS_DEFINED -#define SCOP_MATH_GLM_ALIAS_DEFINED -namespace glm = scop::math; -#endif + #include "math/math.hpp" + #ifndef SCOP_MATH_GLM_ALIAS_DEFINED + #define SCOP_MATH_GLM_ALIAS_DEFINED + namespace glm = scop::math; + #endif #endif #include diff --git a/examples/scop/CMakeLists.txt b/examples/scop/CMakeLists.txt index ab879f9..8285a4a 100644 --- a/examples/scop/CMakeLists.txt +++ b/examples/scop/CMakeLists.txt @@ -155,6 +155,9 @@ if (SCOP_MANDATORY) file(GLOB EXE_SRCS "math/*.cpp" "math/*.hpp" "ObjParser/*.cpp" "ObjParser/*.hpp") target_sources(scop PRIVATE ${EXE_SRCS}) endif() +if(GFX_BUILD_TRACY) + target_sources(scop PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/tracy_allocations.cpp") +endif() target_include_directories(scop PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/examples/scop/Entity/Camera.hpp b/examples/scop/Entity/Camera.hpp index 4e3d93e..4ea544f 100644 --- a/examples/scop/Entity/Camera.hpp +++ b/examples/scop/Entity/Camera.hpp @@ -14,14 +14,14 @@ #include #if !defined (SCOP_MANDATORY) -#include -#include + #include + #include #else -#include "math/math.hpp" -#ifndef SCOP_MATH_GLM_ALIAS_DEFINED -#define SCOP_MATH_GLM_ALIAS_DEFINED -namespace glm = scop::math; -#endif + #include "math/math.hpp" + #ifndef SCOP_MATH_GLM_ALIAS_DEFINED + #define SCOP_MATH_GLM_ALIAS_DEFINED + namespace glm = scop::math; + #endif #endif #include diff --git a/examples/scop/Entity/Entity.hpp b/examples/scop/Entity/Entity.hpp index ced2232..85b1600 100644 --- a/examples/scop/Entity/Entity.hpp +++ b/examples/scop/Entity/Entity.hpp @@ -13,13 +13,13 @@ #include #if !defined (SCOP_MANDATORY) -#include + #include #else -#include "math/math.hpp" -#ifndef SCOP_MATH_GLM_ALIAS_DEFINED -#define SCOP_MATH_GLM_ALIAS_DEFINED -namespace glm = scop::math; -#endif + #include "math/math.hpp" + #ifndef SCOP_MATH_GLM_ALIAS_DEFINED + #define SCOP_MATH_GLM_ALIAS_DEFINED + namespace glm = scop::math; + #endif #endif namespace scop diff --git a/examples/scop/Entity/Light.hpp b/examples/scop/Entity/Light.hpp index 807ab89..1626365 100644 --- a/examples/scop/Entity/Light.hpp +++ b/examples/scop/Entity/Light.hpp @@ -13,13 +13,13 @@ #include "Entity/Entity.hpp" #if !defined (SCOP_MANDATORY) -#include + #include #else -#include "math/math.hpp" -#ifndef SCOP_MATH_GLM_ALIAS_DEFINED -#define SCOP_MATH_GLM_ALIAS_DEFINED -namespace glm = scop::math; -#endif + #include "math/math.hpp" + #ifndef SCOP_MATH_GLM_ALIAS_DEFINED + #define SCOP_MATH_GLM_ALIAS_DEFINED + namespace glm = scop::math; + #endif #endif namespace scop diff --git a/examples/scop/Entity/RenderableEntity.hpp b/examples/scop/Entity/RenderableEntity.hpp index 9cea042..ff73c58 100644 --- a/examples/scop/Entity/RenderableEntity.hpp +++ b/examples/scop/Entity/RenderableEntity.hpp @@ -14,14 +14,14 @@ #include "Mesh.hpp" #if !defined (SCOP_MANDATORY) -#include -#include + #include + #include #else -#include "math/math.hpp" -#ifndef SCOP_MATH_GLM_ALIAS_DEFINED -#define SCOP_MATH_GLM_ALIAS_DEFINED -namespace glm = scop::math; -#endif + #include "math/math.hpp" + #ifndef SCOP_MATH_GLM_ALIAS_DEFINED + #define SCOP_MATH_GLM_ALIAS_DEFINED + namespace glm = scop::math; + #endif #endif #include @@ -37,7 +37,7 @@ class RenderableEntity : public Entity RenderableEntity(const RenderableEntity&) = delete; RenderableEntity(RenderableEntity&&) = default; - RenderableEntity(std::future&& meshFuture) : m_meshFuture(std::move(meshFuture)) {}; + RenderableEntity(std::shared_future meshFuture) : m_meshFuture(std::move(meshFuture)) {}; inline const std::optional& mesh() { @@ -64,7 +64,7 @@ class RenderableEntity : public Entity ~RenderableEntity() override = default; private: - std::future m_meshFuture; + std::shared_future m_meshFuture; std::optional m_mesh; public: diff --git a/examples/scop/Material.cpp b/examples/scop/Material.cpp index 2209f29..c5b7b94 100644 --- a/examples/scop/Material.cpp +++ b/examples/scop/Material.cpp @@ -22,13 +22,13 @@ #include #if !defined (SCOP_MANDATORY) -#include + #include #else -#include "math/math.hpp" -#ifndef SCOP_MATH_GLM_ALIAS_DEFINED -#define SCOP_MATH_GLM_ALIAS_DEFINED -namespace glm = scop::math; -#endif + #include "math/math.hpp" + #ifndef SCOP_MATH_GLM_ALIAS_DEFINED + #define SCOP_MATH_GLM_ALIAS_DEFINED + namespace glm = scop::math; + #endif #endif #include diff --git a/examples/scop/Material.hpp b/examples/scop/Material.hpp index fed1097..ed91707 100644 --- a/examples/scop/Material.hpp +++ b/examples/scop/Material.hpp @@ -22,13 +22,13 @@ #include #if !defined (SCOP_MANDATORY) -#include + #include #else -#include "math/math.hpp" -#ifndef SCOP_MATH_GLM_ALIAS_DEFINED -#define SCOP_MATH_GLM_ALIAS_DEFINED -namespace glm = scop::math; -#endif + #include "math/math.hpp" + #ifndef SCOP_MATH_GLM_ALIAS_DEFINED + #define SCOP_MATH_GLM_ALIAS_DEFINED + namespace glm = scop::math; + #endif #endif #include diff --git a/examples/scop/Mesh.hpp b/examples/scop/Mesh.hpp index 81612a3..188227a 100644 --- a/examples/scop/Mesh.hpp +++ b/examples/scop/Mesh.hpp @@ -15,13 +15,13 @@ #include #if !defined (SCOP_MANDATORY) -#include + #include #else -#include "math/math.hpp" -#ifndef SCOP_MATH_GLM_ALIAS_DEFINED -#define SCOP_MATH_GLM_ALIAS_DEFINED -namespace glm = scop::math; -#endif + #include "math/math.hpp" + #ifndef SCOP_MATH_GLM_ALIAS_DEFINED + #define SCOP_MATH_GLM_ALIAS_DEFINED + namespace glm = scop::math; + #endif #endif #include diff --git a/examples/scop/ObjParser/ObjParser.hpp b/examples/scop/ObjParser/ObjParser.hpp index 66d84ac..a41283d 100644 --- a/examples/scop/ObjParser/ObjParser.hpp +++ b/examples/scop/ObjParser/ObjParser.hpp @@ -12,14 +12,13 @@ #include "math/math.hpp" #ifndef SCOP_MATH_GLM_ALIAS_DEFINED -#define SCOP_MATH_GLM_ALIAS_DEFINED -namespace glm = scop::math; + #define SCOP_MATH_GLM_ALIAS_DEFINED + namespace glm = scop::math; #endif #include #include #include -#include #include namespace scop diff --git a/examples/scop/Renderer.cpp b/examples/scop/Renderer.cpp index a424037..aea6f45 100644 --- a/examples/scop/Renderer.cpp +++ b/examples/scop/Renderer.cpp @@ -17,17 +17,23 @@ #include #if !defined (SCOP_MANDATORY) -#include -#include -#include -#include + #include + #include + #include + #include #else -#include "math/math.hpp" -#ifndef SCOP_MATH_GLM_ALIAS_DEFINED -#define SCOP_MATH_GLM_ALIAS_DEFINED -namespace glm = scop::math; -#endif + #include "math/math.hpp" + #ifndef SCOP_MATH_GLM_ALIAS_DEFINED + #define SCOP_MATH_GLM_ALIAS_DEFINED + namespace glm = scop::math; + #endif #endif +#if defined (GFX_BUILD_TRACY) + #include +#else + #define ZoneScoped + #define ZoneScopedN(x) +#endif // GFX_BUILD_TRACY #include #include @@ -109,6 +115,7 @@ Renderer::Renderer(gfx::Device* device, GLFWwindow* window, gfx::Surface* surfac void Renderer::beginFrame(const glm::mat4x4& viewMatrix, float fov, float near, float far) { + ZoneScoped; if (m_swapchain == nullptr) { int width = 0, height = 0; ::glfwGetFramebufferSize(m_window, &width, &height); @@ -137,7 +144,7 @@ void Renderer::beginFrame(const glm::mat4x4& viewMatrix, float fov, float near, if (cfd.lastCommandBuffer != nullptr) { m_device->waitCommandBuffer(*cfd.lastCommandBuffer); - cfd.lastCommandBuffer.reset(); + cfd.lastCommandBuffer = nullptr; cfd.commandBufferPool->reset(); cfd.parameterBlockPool->reset(); } @@ -159,14 +166,18 @@ void Renderer::beginFrame(const glm::mat4x4& viewMatrix, float fov, float near, *cfd.vpMatrix->content() = projectionMatrix * viewMatrix; #if !defined (SCOP_MANDATORY) - m_device->imguiNewFrame(); - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); + { + ZoneScopedN("imguiNewFrame"); + m_device->imguiNewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + } #endif } void Renderer::addMesh(const Mesh& mesh, const glm::mat4x4& worlTransform) { + ZoneScoped; std::function addSubmesh = [&](const SubMesh& submesh, const glm::mat4x4& transform) { glm::mat4x4 modelMatrix = transform * submesh.transform; @@ -182,6 +193,7 @@ void Renderer::addMesh(const Mesh& mesh, const glm::mat4x4& worlTransform) void Renderer::addPointLight(const glm::vec3& position, const glm::vec3& color) { + ZoneScoped; if (static_cast(cfsd.pointLightCount) >= sizeof(cfsd.pointLights) / sizeof(cfsd.pointLights[0])) return; cfsd.pointLights[cfsd.pointLightCount++] = shader::PointLight{ // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index) @@ -192,6 +204,7 @@ void Renderer::addPointLight(const glm::vec3& position, const glm::vec3& color) void Renderer::endFrame() { + ZoneScoped; #if !defined (SCOP_MANDATORY) ImGui::Render(); #endif @@ -227,6 +240,7 @@ void Renderer::endFrame() commandBuffer->beginRenderPass(framebuffer); { + ZoneScopedN("renderPass"); std::shared_ptr vpMatrixPBlock = cfd.parameterBlockPool->get(vpMatrixBpLayout()); vpMatrixPBlock->setBinding(0, cfd.vpMatrix); @@ -262,7 +276,7 @@ void Renderer::endFrame() commandBuffer->endRenderPass(); commandBuffer->presentDrawable(drawable); - cfd.lastCommandBuffer = commandBuffer; + cfd.lastCommandBuffer = commandBuffer.get(); m_device->submitCommandBuffers(commandBuffer); #if !defined (SCOP_MANDATORY) diff --git a/examples/scop/Renderer.hpp b/examples/scop/Renderer.hpp index 340f86d..c6ad15d 100644 --- a/examples/scop/Renderer.hpp +++ b/examples/scop/Renderer.hpp @@ -22,13 +22,13 @@ #include #if !defined (SCOP_MANDATORY) -#include + #include #else -#include "math/math.hpp" -#ifndef SCOP_MATH_GLM_ALIAS_DEFINED -#define SCOP_MATH_GLM_ALIAS_DEFINED -namespace glm = scop::math; -#endif + #include "math/math.hpp" + #ifndef SCOP_MATH_GLM_ALIAS_DEFINED + #define SCOP_MATH_GLM_ALIAS_DEFINED + namespace glm = scop::math; + #endif #endif #include @@ -89,7 +89,7 @@ class Renderer std::vector // model matrix >>> renderables; - std::shared_ptr lastCommandBuffer = nullptr; + gfx::CommandBuffer* lastCommandBuffer = nullptr; }; gfx::Device* m_device; diff --git a/examples/scop/scop.cpp b/examples/scop/scop.cpp index 9decfa7..0885f80 100644 --- a/examples/scop/scop.cpp +++ b/examples/scop/scop.cpp @@ -34,6 +34,16 @@ namespace glm = scop::math; #endif #endif +#if defined (GFX_BUILD_TRACY) + #include + #include +#else + #define ZoneScoped + #define ZoneScopedN(x) + #define TracyCZoneN(c,x,y) + #define TracyCZoneEnd(c) + #define FrameMark +#endif #include #include // IWYU pragma: keep @@ -59,7 +69,7 @@ #if !defined (SCOP_MANDATORY) #if (defined(__GNUC__) || defined(__clang__)) - #define SCOP_EXPORT __attribute__((visibility("default"))) + #define SCOP_EXPORT __attribute__((used, visibility("default"))) #elif defined(_MSC_VER) #define SCOP_EXPORT __declspec(dllexport) #else @@ -201,7 +211,10 @@ int main(int argc, char** argv) while (true) { + TracyCZoneN(glfwPollEventsCtx, "glfwPollEvents()", true); glfwPollEvents(); + TracyCZoneEnd(glfwPollEventsCtx); + TracyCZoneN(logicCtx, "logic", true); if (glfwWindowShouldClose(window)) break; @@ -260,7 +273,9 @@ int main(int argc, char** argv) if (lightAttachedToCamera != nullptr) lightAttachedToCamera->setPosition(camera->position()); #endif + TracyCZoneEnd(logicCtx); + TracyCZoneN(renderCtx, "rendering", true); renderer.beginFrame(camera->viewMatrix(), camera->fov(), camera->nearPlane(), camera->farPlane()); #if !defined (SCOP_MANDATORY) @@ -353,6 +368,8 @@ int main(int argc, char** argv) } renderer.endFrame(); + TracyCZoneEnd(renderCtx); + FrameMark; } glfwDestroyWindow(window); diff --git a/examples/scop/tracy_allocations.cpp b/examples/scop/tracy_allocations.cpp new file mode 100644 index 0000000..cbff7ac --- /dev/null +++ b/examples/scop/tracy_allocations.cpp @@ -0,0 +1,46 @@ +/* + * --------------------------------------------------- + * globalNewDelete.cpp + * + * Author: Thomas Choquet + * Date: 2025/12/02 08:45:31 + * --------------------------------------------------- + */ + +#ifdef TRACY_ENABLE + +#include + +#include + +void* operator new(std::size_t size) +{ + void* ptr = std::malloc(size); // NOLINT(cppcoreguidelines-owning-memory, cppcoreguidelines-no-malloc) + if (!ptr) + throw std::bad_alloc(); + TracySecureAlloc(ptr, size); + return ptr; +} + +void* operator new[](std::size_t size) +{ + void* ptr = std::malloc(size); // NOLINT(cppcoreguidelines-owning-memory, cppcoreguidelines-no-malloc) + if (!ptr) + throw std::bad_alloc(); + TracySecureAlloc(ptr, size); + return ptr; +} + +void operator delete(void* ptr) noexcept +{ + TracySecureFree(ptr); + std::free(ptr); // NOLINT(cppcoreguidelines-owning-memory, cppcoreguidelines-no-malloc) +} + +void operator delete[](void* ptr) noexcept +{ + TracySecureFree(ptr); + std::free(ptr); // NOLINT(cppcoreguidelines-owning-memory, cppcoreguidelines-no-malloc) +} + +#endif diff --git a/src/Metal/MetalCommandBuffer.hpp b/src/Metal/MetalCommandBuffer.hpp index 1940eb4..901d199 100644 --- a/src/Metal/MetalCommandBuffer.hpp +++ b/src/Metal/MetalCommandBuffer.hpp @@ -72,6 +72,9 @@ class MetalCommandBuffer : public CommandBuffer inline id mtlCommandBuffer() const { return m_mtlCommandBuffer; } inline id commandEncoder() const { return m_commandEncoder; } + inline void setSignaledSharedEventValue(uint64_t v) { m_signaledSharedEventValue = v; } + inline uint64_t signaledSharedEventValue() const { return m_signaledSharedEventValue; } + ~MetalCommandBuffer() override = default; private: @@ -86,6 +89,8 @@ class MetalCommandBuffer : public CommandBuffer std::set> m_usedPBlock; + uint64_t m_signaledSharedEventValue = 0; + public: MetalCommandBuffer& operator = (const MetalCommandBuffer&) = delete; MetalCommandBuffer& operator = (MetalCommandBuffer&&) noexcept; diff --git a/src/Metal/MetalCommandBuffer.mm b/src/Metal/MetalCommandBuffer.mm index 5f4be7b..f2cab9b 100644 --- a/src/Metal/MetalCommandBuffer.mm +++ b/src/Metal/MetalCommandBuffer.mm @@ -14,6 +14,7 @@ #include "Graphics/Buffer.hpp" #include "Graphics/ParameterBlock.hpp" +#include "Metal/MetalDevice.hpp" #include "Metal/MetalCommandBuffer.hpp" #include "Metal/MetalBuffer.hpp" #include "Metal/MetalSampler.hpp" @@ -76,7 +77,7 @@ renderPassDescriptor.depthAttachment.texture = texture->mtltexture(); m_usedTextures.insert(texture); } - + TracyMetalZone(MetalDevice::s_tracyMtlContext, renderPassDescriptor, "renderPass"); m_commandEncoder = [m_mtlCommandBuffer renderCommandEncoderWithDescriptor: renderPassDescriptor]; }} @@ -197,7 +198,9 @@ void MetalCommandBuffer::beginBlitPass() { @autoreleasepool { assert(m_commandEncoder == nil); - m_commandEncoder = [m_mtlCommandBuffer blitCommandEncoder]; + MTLBlitPassDescriptor* blitPassDescriptor = [[MTLBlitPassDescriptor alloc] init]; + TracyMetalZone(MetalDevice::s_tracyMtlContext, blitPassDescriptor, "blitPass"); + m_commandEncoder = [m_mtlCommandBuffer blitCommandEncoderWithDescriptor:blitPassDescriptor]; }} void MetalCommandBuffer::copyBufferToBuffer(const std::shared_ptr& aSrc, const std::shared_ptr& aDst, size_t size) { @autoreleasepool diff --git a/src/Metal/MetalCommandBufferPool.mm b/src/Metal/MetalCommandBufferPool.mm index 39c3cc7..2beb435 100644 --- a/src/Metal/MetalCommandBufferPool.mm +++ b/src/Metal/MetalCommandBufferPool.mm @@ -40,7 +40,7 @@ void MetalCommandBufferPool::reset() { for (auto& commandBuffer : m_usedCommandBuffers) { - *commandBuffer = MetalCommandBuffer(); + *commandBuffer = MetalCommandBuffer(); // this clear the command buffer m_availableCommandBuffers.push_back(std::move(commandBuffer)); } m_usedCommandBuffers.clear(); diff --git a/src/Metal/MetalDevice.hpp b/src/Metal/MetalDevice.hpp index 4f6c63b..55ec017 100644 --- a/src/Metal/MetalDevice.hpp +++ b/src/Metal/MetalDevice.hpp @@ -67,12 +67,17 @@ class MetalDevice : public Device ~MetalDevice() override; +public: + inline static tracy::MetalCtx* s_tracyMtlContext = nullptr; + private: id m_mtlDevice = nil; id m_queue = nil; std::mutex m_submitMtx; std::deque> m_submittedCommandBuffers; + id m_sharedEvent = nil; + uint64_t m_nextSharedEventValue = 1; public: MetalDevice& operator=(const MetalDevice&) = delete; diff --git a/src/Metal/MetalDevice.mm b/src/Metal/MetalDevice.mm index d491d9b..7bb993e 100644 --- a/src/Metal/MetalDevice.mm +++ b/src/Metal/MetalDevice.mm @@ -24,8 +24,6 @@ #include "Metal/MetalDrawable.hpp" #include "Metal/MetalShaderLib.hpp" #include "MetalParameterBlockLayout.hpp" -#include -#include #if defined(GFX_IMGUI_ENABLED) # include "Metal/imgui_impl_metal.h" #endif @@ -41,6 +39,8 @@ : m_mtlDevice(device) { @autoreleasepool { m_queue = [m_mtlDevice newCommandQueue]; + m_sharedEvent = [m_mtlDevice newSharedEvent]; + s_tracyMtlContext = TracyMetalContext(device); }} std::unique_ptr MetalDevice::newSwapchain(const Swapchain::Descriptor& desc) const @@ -118,6 +118,9 @@ auto commandBuffer = std::dynamic_pointer_cast(aCommandBuffer); assert(commandBuffer); + [commandBuffer->mtlCommandBuffer() encodeSignalEvent:m_sharedEvent value:m_nextSharedEventValue]; + commandBuffer->setSignaledSharedEventValue(m_nextSharedEventValue); + m_nextSharedEventValue++; [commandBuffer->mtlCommandBuffer() commit]; m_submittedCommandBuffers.push_back(commandBuffer); }} @@ -130,10 +133,14 @@ void MetalDevice::waitCommandBuffer(const CommandBuffer& aCommandBuffer) { @autoreleasepool { + ZoneScoped; + std::scoped_lock lock(m_submitMtx); + auto waitedIt = std::ranges::find_if(m_submittedCommandBuffers, [&](auto& c){ return c.get() == &aCommandBuffer; }); if (waitedIt != m_submittedCommandBuffers.end()) { - [(*waitedIt)->mtlCommandBuffer() waitUntilCompleted]; + [m_sharedEvent waitUntilSignaledValue:(*waitedIt)->signaledSharedEventValue() timeoutMS:UINT64_MAX]; + TracyMetalCollect(s_tracyMtlContext); m_submittedCommandBuffers.erase(m_submittedCommandBuffers.begin(), std::next(waitedIt)); } }} @@ -147,6 +154,7 @@ MetalDevice::~MetalDevice() { waitIdle(); + TracyMetalDestroy(s_tracyMtlContext); } } diff --git a/src/Metal/MetalSurface.mm b/src/Metal/MetalSurface.mm index d28bd0b..ccceff8 100644 --- a/src/Metal/MetalSurface.mm +++ b/src/Metal/MetalSurface.mm @@ -18,6 +18,7 @@ MetalSurface::MetalSurface(GLFWwindow* glfwWindow) { @autoreleasepool { m_mtlLayer = [CAMetalLayer layer]; + m_mtlLayer.displaySyncEnabled = YES; NSWindow* nswindow = glfwGetCocoaWindow(glfwWindow); // NOLINT nswindow.contentView.layer = m_mtlLayer; diff --git a/src/Metal/MetalSwapchain.mm b/src/Metal/MetalSwapchain.mm index 841419c..bbf7edc 100644 --- a/src/Metal/MetalSwapchain.mm +++ b/src/Metal/MetalSwapchain.mm @@ -34,6 +34,7 @@ std::shared_ptr MetalSwapchain::nextDrawable() { @autoreleasepool { + ZoneScoped; return std::make_shared([m_mtlLayer nextDrawable]); }} diff --git a/src/Metal/imgui_impl_metal.mm b/src/Metal/imgui_impl_metal.mm index 5ef12b5..eb8b855 100644 --- a/src/Metal/imgui_impl_metal.mm +++ b/src/Metal/imgui_impl_metal.mm @@ -577,6 +577,8 @@ static void ImGui_ImplMetal_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) static void ImGui_ImplMetal_RenderWindow(ImGuiViewport* viewport, void*) { ImGuiViewportDataMetal* data = (ImGuiViewportDataMetal*)viewport->RendererUserData; + ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); + MetalContext* ctx = bd->SharedMetalContext; #if TARGET_OS_OSX void* handle = viewport->PlatformHandleRaw ? viewport->PlatformHandleRaw : viewport->PlatformHandle; @@ -595,8 +597,8 @@ static void ImGui_ImplMetal_RenderWindow(ImGuiViewport* viewport, void*) if (data->MetalLayer.contentsScale != fb_scale) { data->MetalLayer.contentsScale = fb_scale; - data->MetalLayer.drawableSize = MakeScaledSize(window.frame.size, fb_scale); } + data->MetalLayer.drawableSize = MakeScaledSize(window.frame.size, (float)window.backingScaleFactor); #endif id drawable = [data->MetalLayer nextDrawable]; @@ -611,7 +613,10 @@ static void ImGui_ImplMetal_RenderWindow(ImGuiViewport* viewport, void*) id commandBuffer = [data->CommandQueue commandBuffer]; id renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; + FramebufferDescriptor* fbdesc = [ctx.framebufferDescriptor copy]; + ctx.framebufferDescriptor.depthPixelFormat = MTLPixelFormatInvalid; ImGui_ImplMetal_RenderDrawData(viewport->DrawData, commandBuffer, renderEncoder); + ctx.framebufferDescriptor = fbdesc; [renderEncoder endEncoding]; [commandBuffer presentDrawable:drawable]; diff --git a/src/Vulkan/VulkanCommandBuffer.cpp b/src/Vulkan/VulkanCommandBuffer.cpp index a340090..2fa236c 100644 --- a/src/Vulkan/VulkanCommandBuffer.cpp +++ b/src/Vulkan/VulkanCommandBuffer.cpp @@ -67,6 +67,7 @@ VulkanCommandBuffer::VulkanCommandBuffer(const VulkanDevice* device, const vk::C void VulkanCommandBuffer::beginRenderPass(const Framebuffer& framebuffer) { + TracyVkZone_begin(VulkanDevice::s_tracyVkContext, m_vkCommandBuffer, "renderPass", m_tracyVkCtxScope, true); std::vector colorAttachmentInfos(framebuffer.colorAttachments.size()); std::optional depthAttachmentInfo; std::vector imageMemoryBarriers; @@ -314,7 +315,7 @@ void VulkanCommandBuffer::setParameterBlock(const std::shared_ptrpipelineLayout(), vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, 0, size, data); + m_vkCommandBuffer.pushConstants(m_boundPipeline->pipelineLayout(), vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, 0, (uint32_t)size, data); } void VulkanCommandBuffer::drawVertices(uint32_t start, uint32_t count) @@ -364,6 +365,7 @@ void VulkanCommandBuffer::imGuiRenderDrawData(ImDrawData* drawData) const void VulkanCommandBuffer::endRenderPass() { m_vkCommandBuffer.endRendering(); + TracyVkZone_end(m_tracyVkCtxScope); } void VulkanCommandBuffer::beginBlitPass() @@ -463,10 +465,10 @@ void VulkanCommandBuffer::copyBufferToTexture(const std::shared_ptr& aBu bufferMemoryBarriers.push_back(*barrier); } } else { - auto [it1, res1] = m_bufferSyncRequests.insert(std::make_pair(buffer, bufferSyncReq)); + auto res1 = m_bufferSyncRequests.insert(std::make_pair(buffer, bufferSyncReq)).second; assert(res1); (void)res1; - auto [it2, res2] = m_bufferFinalSyncStates.insert(std::make_pair(buffer, bufferStateAfterSync(bufferSyncReq))); + auto res2 = m_bufferFinalSyncStates.insert(std::make_pair(buffer, bufferStateAfterSync(bufferSyncReq))).second; assert(res2); (void)res2; } @@ -486,10 +488,10 @@ void VulkanCommandBuffer::copyBufferToTexture(const std::shared_ptr& aBu imageMemoryBarriers.push_back(*barrier); } } else { - auto [it1, res1] = m_imageSyncRequests.insert(std::make_pair(texture, imageSyncReq)); + auto res1 = m_imageSyncRequests.insert(std::make_pair(texture, imageSyncReq)).second; assert(res1); (void)res1; - auto [it2, res2] = m_imageFinalSyncStates.insert(std::make_pair(texture, imageStateAfterSync(imageSyncReq))); + auto res2 = m_imageFinalSyncStates.insert(std::make_pair(texture, imageStateAfterSync(imageSyncReq))).second; assert(res2); (void)res2; } diff --git a/src/Vulkan/VulkanCommandBuffer.hpp b/src/Vulkan/VulkanCommandBuffer.hpp index 0044515..8dbcc3c 100644 --- a/src/Vulkan/VulkanCommandBuffer.hpp +++ b/src/Vulkan/VulkanCommandBuffer.hpp @@ -109,6 +109,10 @@ class VulkanCommandBuffer : public CommandBuffer } m_nonReusedRessources; +#if defined(TRACY_ENABLE) + std::shared_ptr m_tracyVkCtxScope = nullptr; +#endif + public: VulkanCommandBuffer& operator=(const VulkanCommandBuffer&) = delete; VulkanCommandBuffer& operator=(VulkanCommandBuffer&&) = delete; diff --git a/src/Vulkan/VulkanDevice.cpp b/src/Vulkan/VulkanDevice.cpp index 7a7df44..b57f9b8 100644 --- a/src/Vulkan/VulkanDevice.cpp +++ b/src/Vulkan/VulkanDevice.cpp @@ -107,6 +107,13 @@ VulkanDevice::VulkanDevice(const VulkanInstance* instance, const VulkanPhysicalD m_barrierCommandPool = m_vkDevice.createCommandPool(vk::CommandPoolCreateInfo{} .setQueueFamilyIndex(m_queueFamily.index) .setFlags(vk::CommandPoolCreateFlagBits::eResetCommandBuffer)); + + s_tracyVkContext = TracyVkContextHostCalibrated( + m_instance->vkInstance(), + static_cast(*m_physicalDevice), + m_vkDevice, + (PFN_vkGetInstanceProcAddr)VULKAN_HPP_DEFAULT_DISPATCHER.vkGetInstanceProcAddr, + (PFN_vkGetDeviceProcAddr)VULKAN_HPP_DEFAULT_DISPATCHER.vkGetDeviceProcAddr); } std::unique_ptr VulkanDevice::newSwapchain(const Swapchain::Descriptor& desc) const @@ -323,7 +330,10 @@ void VulkanDevice::submitCommandBuffers(const std::vector barrierCmdBuffer = getBarrierCommandBuffer(); - barrierCmdBuffer->vkCommandBuffer().pipelineBarrier2(dependencyInfo); + { + TracyVkZone(s_tracyVkContext, commandBuffer->vkCommandBuffer(), "barrierCmdBuffer"); + barrierCmdBuffer->vkCommandBuffer().pipelineBarrier2(dependencyInfo); + } barrierCmdBuffer->end(); // barrierCmdBuffer is added before the user command buffer barrierCmdBuffer->setSignaledTimeValue(m_nextSignaledTimeValue); @@ -376,6 +386,7 @@ void VulkanDevice::submitCommandBuffers(const std::vectorsignaledTimeValue()); if (m_vkDevice.waitSemaphores(semaphoreWaitInfo, std::numeric_limits::max()) != vk::Result::eSuccess) throw std::runtime_error("failed to wait timeline semaphore"); + TracyVkCollectHost(s_tracyVkContext); for (auto it = m_submittedCommandBuffers.begin(); it != waitedIt; ++it) { if (m_usedBarrierCmdBuffers.contains(*it)) { auto node = m_usedBarrierCmdBuffers.extract(*it); @@ -415,6 +427,7 @@ void VulkanDevice::waitIdle() VulkanDevice::~VulkanDevice() { waitIdle(); + TracyVkDestroy(s_tracyVkContext); m_vkDevice.destroyCommandPool(m_barrierCommandPool); m_vkDevice.destroySemaphore(m_timelineSemaphore); vmaDestroyAllocator(m_allocator); @@ -429,6 +442,8 @@ std::shared_ptr VulkanDevice::getBarrierCommandBuffer() m_availableBarrierCmdBuffers.pop_front(); } else { + // command buffer is implicitly reset when begin is called. + // https://docs.vulkan.org/refpages/latest/refpages/source/VkCommandPoolCreateFlagBits.html#_description commandBuffer = std::make_shared(this, m_barrierCommandPool); } m_usedBarrierCmdBuffers.insert(commandBuffer); diff --git a/src/Vulkan/VulkanDevice.hpp b/src/Vulkan/VulkanDevice.hpp index a2d6038..5c4288b 100644 --- a/src/Vulkan/VulkanDevice.hpp +++ b/src/Vulkan/VulkanDevice.hpp @@ -77,6 +77,9 @@ class VulkanDevice : public Device ~VulkanDevice() override; +public: + inline static tracy::VkCtx* s_tracyVkContext = nullptr; + private: const VulkanInstance* const m_instance = nullptr; const VulkanPhysicalDevice* const m_physicalDevice = nullptr; diff --git a/src/Vulkan/VulkanInstance.cpp b/src/Vulkan/VulkanInstance.cpp index 639e5be..448ec90 100644 --- a/src/Vulkan/VulkanInstance.cpp +++ b/src/Vulkan/VulkanInstance.cpp @@ -183,6 +183,7 @@ std::unique_ptr VulkanInstance::newDevice(const Device::Descriptor& desc .deviceExtensions = { VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME, vk::KHRSynchronization2ExtensionName, + vk::EXTCalibratedTimestampsExtensionName } }; diff --git a/src/Vulkan/VulkanParameterBlock.cpp b/src/Vulkan/VulkanParameterBlock.cpp index 7a5fed5..1df104b 100644 --- a/src/Vulkan/VulkanParameterBlock.cpp +++ b/src/Vulkan/VulkanParameterBlock.cpp @@ -32,11 +32,12 @@ VulkanParameterBlock::VulkanParameterBlock(const VulkanDevice* device, const std .setDescriptorSetCount(1) .setSetLayouts(m_layout->vkDescriptorSetLayout()); - std::vector descriptorSets = m_device->vkDevice().allocateDescriptorSets(descriptorSetAllocateInfo); - if (descriptorSets.empty()) + try { + std::vector descriptorSets = m_device->vkDevice().allocateDescriptorSets(descriptorSetAllocateInfo); + m_descriptorSet = std::move(descriptorSets.front()); + } catch (...){ throw std::runtime_error("failed to allocate descriptorSet"); - - m_descriptorSet = std::move(descriptorSets.front()); + } } void VulkanParameterBlock::setBinding(uint32_t idx, const std::shared_ptr& aBuffer) diff --git a/src/Vulkan/VulkanSwapchain.cpp b/src/Vulkan/VulkanSwapchain.cpp index fde7a15..3be1f13 100644 --- a/src/Vulkan/VulkanSwapchain.cpp +++ b/src/Vulkan/VulkanSwapchain.cpp @@ -94,6 +94,7 @@ VulkanSwapchain::VulkanSwapchain(const VulkanDevice* device, const Descriptor& d std::shared_ptr VulkanSwapchain::nextDrawable() { + ZoneScoped; std::shared_ptr drawable = m_drawables.at(m_nextDrawableIndex); uint64_t timeout = std::numeric_limits::max(); diff --git a/src/pch.hpp b/src/pch.hpp index 4339c46..621deb5 100644 --- a/src/pch.hpp +++ b/src/pch.hpp @@ -44,7 +44,7 @@ #endif // __OBJC__ #endif // GFX_BUILD_METAL -#if defined (GFX_BUILD_VULKAN) +#if defined(GFX_BUILD_VULKAN) #include @@ -90,10 +90,48 @@ struct GLFWwindow; #endif // GFX_GLFW_ENABLED -#if defined (GFX_IMGUI_ENABLED) +#if defined(GFX_IMGUI_ENABLED) #include "imgui.h" // IWYU pragma: keep #endif // GFX_IMGUI_ENABLED +// NOLINTBEGIN(cppcoreguidelines-macro-usage) +#if defined(GFX_BUILD_TRACY) + #include + #include +#else + #define ZoneScoped + #define ZoneScopedN(x) +#endif + +#if defined(GFX_BUILD_TRACY) && defined(GFX_BUILD_METAL) && defined(__OBJC__) && (defined(__aarch64__) || defined(__arm64__)) + #include +#else + #define TracyMetalContext(device) nullptr + #define TracyMetalDestroy(ctx) + #define TracyMetalZone(ctx, encoderDesc, name) + #define TracyMetalCollect(ctx) + namespace tracy { class MetalCtx; } +#endif +#if defined(GFX_BUILD_TRACY) && defined(GFX_BUILD_VULKAN) + #define TRACY_VK_USE_SYMBOL_TABLE + #include + #define TracyVkZone_begin(ctx, cmdbuf, name, variable, active) \ + static constexpr tracy::SourceLocationData TracyConcat(__tracy_gpu_source_location, TracyLine){ \ + name, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 \ + }; \ + (variable) = std::make_shared(ctx, &TracyConcat(__tracy_gpu_source_location, TracyLine), cmdbuf, active); + #define TracyVkZone_end(variable) variable.reset() +#else + #define TracyVkContextHostCalibrated(instance, physdev, device, instanceProcAddr, deviceProcAddr) nullptr + #define TracyVkZone(ctx, cmdbuf, name) + #define TracyVkCollectHost(ctx) + #define TracyVkDestroy(ctx) + namespace tracy { class VkCtx; } + #define TracyVkZone_begin(ctx, cmdbuf, name, variable, active) + #define TracyVkZone_end(variable) +#endif +// NOLINTEND(cppcoreguidelines-macro-usage) + #endif // GRAPHICS_PCH_HPP