Skip to content

Commit

Permalink
Add explicit frame capture (#1934)
Browse files Browse the repository at this point in the history
  • Loading branch information
TimSylvester authored Dec 9, 2023
1 parent ea73a57 commit de1bcd1
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 1 deletion.
2 changes: 2 additions & 0 deletions include/mbgl/mtl/mtl_fwd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace MTL {
class BlitCommandEncoder;
class BlitPassDescriptor;
class Buffer;
class CaptureScope;
class CommandBuffer;
class CommandQueue;
class Device;
Expand All @@ -43,6 +44,7 @@ using CAMetalDrawablePtr = NS::SharedPtr<CA::MetalDrawable>;
using MTLBlitCommandEncoderPtr = NS::SharedPtr<MTL::BlitCommandEncoder>;
using MTLBlitPassDescriptorPtr = NS::SharedPtr<MTL::BlitPassDescriptor>;
using MTLBufferPtr = NS::SharedPtr<MTL::Buffer>;
using MTLCaptureScopePtr = NS::SharedPtr<MTL::CaptureScope>;
using MTLCommandBufferPtr = NS::SharedPtr<MTL::CommandBuffer>;
using MTLCommandQueuePtr = NS::SharedPtr<MTL::CommandQueue>;
using MTLDevicePtr = NS::SharedPtr<MTL::Device>;
Expand Down
85 changes: 84 additions & 1 deletion src/mbgl/renderer/renderer_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,16 @@
#include <limits>
#endif // MLN_DRAWABLE_RENDERER

#if !MLN_RENDER_BACKEND_METAL
#if MLN_RENDER_BACKEND_METAL
#include <mbgl/mtl/renderer_backend.hpp>
#include <Metal/MTLCaptureManager.hpp>
#include <Metal/MTLCaptureScope.hpp>
/// Enable programmatic Metal frame captures for specific frame numbers.
/// Requries iOS 13
constexpr auto EnableMetalCapture = 0;
constexpr auto CaptureFrameStart = 0; // frames are 0-based
constexpr auto CaptureFrameCount = 1;
#else // !MLN_RENDER_BACKEND_METAL
#include <mbgl/gl/defines.hpp>
#if MLN_DRAWABLE_RENDERER
#include <mbgl/gl/drawable_gl.hpp>
Expand Down Expand Up @@ -64,6 +73,67 @@ void Renderer::Impl::setObserver(RendererObserver* observer_) {
void Renderer::Impl::render(const RenderTree& renderTree,
[[maybe_unused]] const std::shared_ptr<UpdateParameters>& updateParameters) {
auto& context = backend.getContext();
#if MLN_RENDER_BACKEND_METAL
if constexpr (EnableMetalCapture) {
const auto& mtlBackend = static_cast<mtl::RendererBackend&>(backend);
const auto& mtlDevice = mtlBackend.getDevice();

if (!commandCaptureScope) {
if (const auto& cmdQueue = mtlBackend.getCommandQueue()) {
if (const auto captureManager = NS::RetainPtr(MTL::CaptureManager::sharedCaptureManager())) {
if ((commandCaptureScope = NS::TransferPtr(captureManager->newCaptureScope(cmdQueue.get())))) {
const auto label = "Renderer::Impl frame=" + util::toString(frameCount);
commandCaptureScope->setLabel(NS::String::string(label.c_str(), NS::UTF8StringEncoding));
captureManager->setDefaultCaptureScope(commandCaptureScope.get());
}
}
}
}

// "When you capture a frame programmatically, you can capture Metal commands that span multiple
// frames by using a custom capture scope. For example, by calling begin() at the start of frame
// 1 and end() after frame 3, the trace will contain command data from all the buffers that were
// committed in the three frames."
// https://developer.apple.com/documentation/metal/debugging_tools/capturing_gpu_command_data_programmatically
if constexpr (0 < CaptureFrameStart && 0 < CaptureFrameCount) {
if (commandCaptureScope) {
const auto captureManager = NS::RetainPtr(MTL::CaptureManager::sharedCaptureManager());
if (frameCount == CaptureFrameStart) {
constexpr auto captureDest = MTL::CaptureDestination::CaptureDestinationDeveloperTools;
if (captureManager && !captureManager->isCapturing() &&
captureManager->supportsDestination(captureDest)) {
if (auto captureDesc = NS::TransferPtr(MTL::CaptureDescriptor::alloc()->init())) {
captureDesc->setCaptureObject(mtlDevice.get());
captureDesc->setDestination(captureDest);
NS::Error* errorPtr = nullptr;
if (captureManager->startCapture(captureDesc.get(), &errorPtr)) {
Log::Warning(Event::Render, "Capture Started");
} else {
std::string errStr = "<none>";
if (auto error = NS::TransferPtr(errorPtr)) {
if (auto str = error->localizedDescription()) {
if (auto cstr = str->utf8String()) {
errStr = cstr;
}
}
}
Log::Warning(Event::Render, "Capture Failed: " + errStr);
}
}
}
}
}
}
if (commandCaptureScope) {
commandCaptureScope->beginScope();

const auto captureManager = NS::RetainPtr(MTL::CaptureManager::sharedCaptureManager());
if (captureManager->isCapturing()) {
Log::Info(Event::Render, "Capturing frame " + util::toString(frameCount));
}
}
}
#endif // MLN_RENDER_BACKEND_METAL

// Blocks execution until the renderable is available.
backend.getDefaultRenderable().wait();
Expand Down Expand Up @@ -402,6 +472,19 @@ void Renderer::Impl::render(const RenderTree& renderTree,
// CommandEncoder destructor submits render commands.
parameters.encoder.reset();

#if MLN_RENDER_BACKEND_METAL
if constexpr (EnableMetalCapture) {
if (commandCaptureScope) {
commandCaptureScope->endScope();

const auto captureManager = NS::RetainPtr(MTL::CaptureManager::sharedCaptureManager());
if (frameCount == CaptureFrameStart + CaptureFrameCount - 1 && captureManager->isCapturing()) {
captureManager->stopCapture();
}
}
}
#endif // MLN_RENDER_BACKEND_METAL

const auto encodingTime = renderTree.getElapsedTime() - renderingTime;

observer->onDidFinishRenderingFrame(
Expand Down
9 changes: 9 additions & 0 deletions src/mbgl/renderer/renderer_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

#include <mbgl/renderer/render_orchestrator.hpp>

#if MLN_RENDER_BACKEND_METAL
#include <mbgl/mtl/mtl_fwd.hpp>
#include <Foundation/Foundation.hpp>
#endif // MLN_RENDER_BACKEND_METAL

#include <memory>
#include <string>

Expand Down Expand Up @@ -49,6 +54,10 @@ class Renderer::Impl {
RenderState renderState = RenderState::Never;

uint64_t frameCount = 0;

#if MLN_RENDER_BACKEND_METAL
mtl::MTLCaptureScopePtr commandCaptureScope;
#endif // MLN_RENDER_BACKEND_METAL
};

} // namespace mbgl

0 comments on commit de1bcd1

Please sign in to comment.