Commit 1393bf9e by Jamie Madill Committed by Commit Bot

Vulkan: Timestamp internal RenderPasses in perf tests.

This change book-ends RenderPasses in the Vulkan back-end with timestamp queries. This allows us to write out a trace of GPU/CPU that we can inspect in Chrome. To enable the GPU trace you can define ANGLE_ENABLE_VULKAN_GPU_TRACE_EVENTS in ContextVk.cpp and run the angle_perftests suite with --enable-trace. Note that we ran into some issues with the implementation not returning monotonic timestamps. This may be a driver bug but bears more investigation. Bug: angleproject:4433 Change-Id: I0e6a364367f15183068e55686549cb418aa94c2a Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2081380Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent a8c95f74
......@@ -267,6 +267,13 @@ SurfaceRotationType DetermineSurfaceRotation(gl::Framebuffer *framebuffer,
}
}
// Should not generate a copy with modern C++.
EventName GetTraceEventName(const char *title, uint32_t counter)
{
EventName buf;
snprintf(buf.data(), kMaxGpuEventNameLen - 1, "%s %u", title, counter);
return buf;
}
} // anonymous namespace
ContextVk::DriverUniformsDescriptorSet::DriverUniformsDescriptorSet()
......@@ -601,9 +608,12 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk
mEmulateSeamfulCubeMapSampling(false),
mUseOldRewriteStructSamplers(false),
mPoolAllocator(kDefaultPoolAllocatorPageSize, 1),
mHasPrimaryCommands(false),
mGpuEventsEnabled(false),
mGpuClockSync{std::numeric_limits<double>::max(), std::numeric_limits<double>::max()},
mGpuEventTimestampOrigin(0),
mPrimaryBufferCounter(0),
mRenderPassCounter(0),
mContextPriority(renderer->getDriverPriority(GetContextPriority(state)))
{
ANGLE_TRACE_EVENT0("gpu.angle", "ContextVk::ContextVk");
......@@ -801,6 +811,16 @@ angle::Result ContextVk::initialize()
mGpuEventsEnabled = gpuEventsEnabled && *gpuEventsEnabled;
#endif
mEmulateSeamfulCubeMapSampling = shouldEmulateSeamfulCubeMapSampling();
mUseOldRewriteStructSamplers = shouldUseOldRewriteStructSamplers();
// Push a scope in the pool allocator so we can easily reinitialize on flush.
mPoolAllocator.push();
mOutsideRenderPassCommands.getCommandBuffer().initialize(&mPoolAllocator);
mRenderPassCommands.initialize(&mPoolAllocator);
ANGLE_TRY(startPrimaryCommandBuffer());
if (mGpuEventsEnabled)
{
// GPU events should only be available if timestamp queries are available.
......@@ -809,17 +829,12 @@ angle::Result ContextVk::initialize()
ANGLE_TRY(mGpuEventQueryPool.init(this, VK_QUERY_TYPE_TIMESTAMP,
vk::kDefaultTimestampQueryPoolSize));
ANGLE_TRY(synchronizeCpuGpuTime());
}
mEmulateSeamfulCubeMapSampling = shouldEmulateSeamfulCubeMapSampling();
mPrimaryBufferCounter++;
mUseOldRewriteStructSamplers = shouldUseOldRewriteStructSamplers();
// Push a scope in the pool allocator so we can easily reinitialize on flush.
mPoolAllocator.push();
mOutsideRenderPassCommands.getCommandBuffer().initialize(&mPoolAllocator);
mRenderPassCommands.initialize(&mPoolAllocator);
ANGLE_TRY(startPrimaryCommandBuffer());
EventName eventName = GetTraceEventName("Primary", mPrimaryBufferCounter);
ANGLE_TRY(traceGpuEvent(&mPrimaryCommands, TRACE_EVENT_PHASE_BEGIN, eventName));
}
return angle::Result::Continue;
}
......@@ -833,6 +848,8 @@ angle::Result ContextVk::startPrimaryCommandBuffer()
beginInfo.flags = 0;
beginInfo.pInheritanceInfo = nullptr;
ANGLE_VK_TRY(this, mPrimaryCommands.begin(beginInfo));
mHasPrimaryCommands = false;
return angle::Result::Continue;
}
......@@ -1108,7 +1125,7 @@ angle::Result ContextVk::setupDispatch(const gl::Context *context,
// |setupDispatch| and |setupDraw| are special in that they flush dirty bits. Therefore they
// don't use the same APIs to record commands as the functions outside ContextVk.
// The following ensures prior commands are flushed before we start processing dirty bits.
mOutsideRenderPassCommands.flushToPrimary(this, &mPrimaryCommands);
flushOutsideRenderPassCommands();
ANGLE_TRY(endRenderPass());
*commandBufferOut = &mOutsideRenderPassCommands.getCommandBuffer();
......@@ -1683,7 +1700,7 @@ angle::Result ContextVk::synchronizeCpuGpuTime()
angle::Result ContextVk::traceGpuEventImpl(vk::PrimaryCommandBuffer *commandBuffer,
char phase,
const char *name)
const EventName &name)
{
ASSERT(mGpuEventsEnabled);
......@@ -1747,7 +1764,7 @@ angle::Result ContextVk::checkCompletedGpuEvents()
void ContextVk::flushGpuEvents(double nextSyncGpuTimestampS, double nextSyncCpuTimestampS)
{
if (mGpuEvents.size() == 0)
if (mGpuEvents.empty())
{
return;
}
......@@ -1772,10 +1789,10 @@ void ContextVk::flushGpuEvents(double nextSyncGpuTimestampS, double nextSyncCpuT
gpuSyncDriftSlope =
(nextGpuSyncDiffS - lastGpuSyncDiffS) / (nextGpuSyncTimeS - lastGpuSyncTimeS);
for (const GpuEvent &event : mGpuEvents)
for (const GpuEvent &gpuEvent : mGpuEvents)
{
double gpuTimestampS =
(event.gpuTimestampCycles - mGpuEventTimestampOrigin) *
(gpuEvent.gpuTimestampCycles - mGpuEventTimestampOrigin) *
static_cast<double>(
getRenderer()->getPhysicalDeviceProperties().limits.timestampPeriod) *
1e-9;
......@@ -1788,8 +1805,9 @@ void ContextVk::flushGpuEvents(double nextSyncGpuTimestampS, double nextSyncCpuT
static long long eventId = 1;
static const unsigned char *categoryEnabled =
TRACE_EVENT_API_GET_CATEGORY_ENABLED(platform, "gpu.angle.gpu");
platform->addTraceEvent(platform, event.phase, categoryEnabled, event.name, eventId++,
gpuTimestampS, 0, nullptr, nullptr, nullptr, TRACE_EVENT_FLAG_NONE);
platform->addTraceEvent(platform, gpuEvent.phase, categoryEnabled, gpuEvent.name.data(),
eventId++, gpuTimestampS, 0, nullptr, nullptr, nullptr,
TRACE_EVENT_FLAG_NONE);
}
mGpuEvents.clear();
......@@ -2197,7 +2215,7 @@ angle::Result ContextVk::clearWithRenderPassOp(
}
else
{
mOutsideRenderPassCommands.flushToPrimary(this, &mPrimaryCommands);
flushOutsideRenderPassCommands();
}
size_t attachmentIndexVk = 0;
......@@ -3620,20 +3638,20 @@ bool ContextVk::shouldFlush()
bool ContextVk::hasRecordedCommands()
{
return !mOutsideRenderPassCommands.empty() || !mRenderPassCommands.empty() ||
!mPrimaryCommands.empty();
mHasPrimaryCommands;
}
angle::Result ContextVk::flushImpl(const vk::Semaphore *signalSemaphore)
{
bool hasPendingSemaphore = signalSemaphore || !mWaitSemaphores.empty();
if (!hasRecordedCommands() && !hasPendingSemaphore)
if (!hasRecordedCommands() && !hasPendingSemaphore && !mGpuEventsEnabled)
{
return angle::Result::Continue;
}
ANGLE_TRACE_EVENT0("gpu.angle", "ContextVk::flush");
mOutsideRenderPassCommands.flushToPrimary(this, &mPrimaryCommands);
flushOutsideRenderPassCommands();
ANGLE_TRY(endRenderPass());
if (mIsAnyHostVisibleBufferWritten)
......@@ -3651,6 +3669,12 @@ angle::Result ContextVk::flushImpl(const vk::Semaphore *signalSemaphore)
mIsAnyHostVisibleBufferWritten = false;
}
if (mGpuEventsEnabled)
{
EventName eventName = GetTraceEventName("Primary", mPrimaryBufferCounter);
ANGLE_TRY(traceGpuEvent(&mPrimaryCommands, TRACE_EVENT_PHASE_END, eventName));
}
ANGLE_VK_TRY(this, mPrimaryCommands.end());
// Free secondary command pool allocations and restart command buffers with the new page.
......@@ -3672,8 +3696,17 @@ angle::Result ContextVk::flushImpl(const vk::Semaphore *signalSemaphore)
ANGLE_TRY(startPrimaryCommandBuffer());
mRenderPassCounter = 0;
mWaitSemaphores.clear();
mPrimaryBufferCounter++;
if (mGpuEventsEnabled)
{
EventName eventName = GetTraceEventName("Primary", mPrimaryBufferCounter);
ANGLE_TRY(traceGpuEvent(&mPrimaryCommands, TRACE_EVENT_PHASE_BEGIN, eventName));
}
return angle::Result::Continue;
}
......@@ -3943,7 +3976,7 @@ angle::Result ContextVk::onBufferRead(VkAccessFlags readAccessType, vk::BufferHe
if (!buffer->canAccumulateRead(this, readAccessType))
{
mOutsideRenderPassCommands.flushToPrimary(this, &mPrimaryCommands);
flushOutsideRenderPassCommands();
}
mOutsideRenderPassCommands.bufferRead(&mResourceUseList, readAccessType, buffer);
......@@ -3957,7 +3990,7 @@ angle::Result ContextVk::onBufferWrite(VkAccessFlags writeAccessType, vk::Buffer
if (!buffer->canAccumulateWrite(this, writeAccessType))
{
mOutsideRenderPassCommands.flushToPrimary(this, &mPrimaryCommands);
flushOutsideRenderPassCommands();
}
mOutsideRenderPassCommands.bufferWrite(&mResourceUseList, writeAccessType, buffer);
......@@ -4027,7 +4060,28 @@ angle::Result ContextVk::flushAndBeginRenderPass(
angle::Result ContextVk::endRenderPass()
{
onRenderPassFinished();
return mRenderPassCommands.flushToPrimary(this, &mPrimaryCommands);
if (mRenderPassCommands.empty())
{
return angle::Result::Continue;
}
if (mGpuEventsEnabled)
{
mRenderPassCounter++;
EventName eventName = GetTraceEventName("RP", mRenderPassCounter);
ANGLE_TRY(traceGpuEvent(&mPrimaryCommands, TRACE_EVENT_PHASE_BEGIN, eventName));
}
ANGLE_TRY(mRenderPassCommands.flushToPrimary(this, &mPrimaryCommands));
if (mGpuEventsEnabled)
{
EventName eventName = GetTraceEventName("RP", mRenderPassCounter);
ANGLE_TRY(traceGpuEvent(&mPrimaryCommands, TRACE_EVENT_PHASE_END, eventName));
}
return angle::Result::Continue;
}
void ContextVk::onRenderPassImageWrite(VkImageAspectFlags aspectFlags,
......@@ -4115,6 +4169,15 @@ bool ContextVk::shouldConvertUint8VkIndexType(gl::DrawElementsType glIndexType)
!mRenderer->getFeatures().supportsIndexTypeUint8.enabled);
}
void ContextVk::flushOutsideRenderPassCommands()
{
if (!mOutsideRenderPassCommands.empty())
{
mOutsideRenderPassCommands.flushToPrimary(this, &mPrimaryCommands);
mHasPrimaryCommands = true;
}
}
CommandBufferHelper::CommandBufferHelper()
: mImageBarrierSrcStageMask(0),
mImageBarrierDstStageMask(0),
......
......@@ -249,6 +249,9 @@ class RenderPassCommandBuffer final : public CommandBufferHelper
bool mRebindTransformFeedbackBuffers;
};
static constexpr uint32_t kMaxGpuEventNameLen = 32;
using EventName = std::array<char, kMaxGpuEventNameLen>;
class ContextVk : public ContextImpl, public vk::Context
{
public:
......@@ -568,7 +571,7 @@ class ContextVk : public ContextImpl, public vk::Context
// are TRACE_EVENT_PHASE_*
ANGLE_INLINE angle::Result traceGpuEvent(vk::PrimaryCommandBuffer *commandBuffer,
char phase,
const char *name)
const EventName &name)
{
if (mGpuEventsEnabled)
return traceGpuEventImpl(commandBuffer, phase, name);
......@@ -637,9 +640,12 @@ class ContextVk : public ContextImpl, public vk::Context
angle::Result flushAndGetPrimaryCommandBuffer(vk::PrimaryCommandBuffer **primaryCommands)
{
mOutsideRenderPassCommands.flushToPrimary(this, &mPrimaryCommands);
flushOutsideRenderPassCommands();
ANGLE_TRY(endRenderPass());
*primaryCommands = &mPrimaryCommands;
// We assume any calling code is going to record primary commands.
mHasPrimaryCommands = true;
return angle::Result::Continue;
}
......@@ -713,7 +719,7 @@ class ContextVk : public ContextImpl, public vk::Context
// submitted, the query is not checked to avoid incuring a flush.
struct GpuEventQuery final
{
const char *name;
EventName name;
char phase;
vk::QueryHelper queryHelper;
};
......@@ -724,7 +730,7 @@ class ContextVk : public ContextImpl, public vk::Context
struct GpuEvent final
{
uint64_t gpuTimestampCycles;
const char *name;
std::array<char, kMaxGpuEventNameLen> name;
char phase;
};
......@@ -888,13 +894,12 @@ class ContextVk : public ContextImpl, public vk::Context
angle::Result submitFrame(const VkSubmitInfo &submitInfo,
vk::PrimaryCommandBuffer &&commandBuffer);
angle::Result flushCommandGraph(vk::PrimaryCommandBuffer *commandBatch);
angle::Result memoryBarrierImpl(GLbitfield barriers, VkPipelineStageFlags stageMask);
angle::Result synchronizeCpuGpuTime();
angle::Result traceGpuEventImpl(vk::PrimaryCommandBuffer *commandBuffer,
char phase,
const char *name);
const EventName &name);
angle::Result checkCompletedGpuEvents();
void flushGpuEvents(double nextSyncGpuTimestampS, double nextSyncCpuTimestampS);
void handleDeviceLost();
......@@ -906,6 +911,7 @@ class ContextVk : public ContextImpl, public vk::Context
angle::Result startPrimaryCommandBuffer();
bool hasRecordedCommands();
void dumpCommandStreamDiagnostics();
void flushOutsideRenderPassCommands();
ANGLE_INLINE void onRenderPassFinished() { mRenderPassCommandBuffer = nullptr; }
......@@ -1028,6 +1034,7 @@ class ContextVk : public ContextImpl, public vk::Context
OutsideRenderPassCommandBuffer mOutsideRenderPassCommands;
RenderPassCommandBuffer mRenderPassCommands;
vk::PrimaryCommandBuffer mPrimaryCommands;
bool mHasPrimaryCommands;
// Internal shader library.
vk::ShaderLibrary mShaderLibrary;
......@@ -1053,6 +1060,10 @@ class ContextVk : public ContextImpl, public vk::Context
// double.
uint64_t mGpuEventTimestampOrigin;
// Used to count events for tracing.
uint32_t mPrimaryBufferCounter;
uint32_t mRenderPassCounter;
// Generators for texture & framebuffer serials.
SerialFactory mTextureSerialFactory;
SerialFactory mAttachmentImageSerialFactory;
......
......@@ -263,6 +263,11 @@ if (is_win || is_linux || is_android || is_mac || is_fuchsia) {
data_deps += [ "${angle_root}/third_party/glmark2:glmark2_glx" ]
}
if (is_clang) {
# Allows us to edit traces and remove chunks of code without compile warnings.
cflags_cc = [ "-Wno-unused-const-variable" ]
}
if (build_angle_trace_perf_tests) {
import("perf_tests/restricted_traces/angle_trace_perf_tests.gni")
sources += angle_trace_perf_sources
......
......@@ -138,8 +138,8 @@ void DumpTraceEventsToJSONFile(const std::vector<TraceEvent> &traceEvents,
value["cat"] = traceEvent.categoryName;
value["ph"] = phaseName.str();
value["ts"] = microseconds;
value["pid"] = "ANGLE";
value["tid"] = strcmp(traceEvent.categoryName, "gpu.angle.gpu") == 0 ? "GPU" : "CPU";
value["pid"] = strcmp(traceEvent.categoryName, "gpu.angle.gpu") == 0 ? "GPU" : "ANGLE";
value["tid"] = 1;
eventsValue.append(value);
}
......@@ -175,7 +175,7 @@ TraceEvent::TraceEvent(char phaseIn,
const char *categoryNameIn,
const char *nameIn,
double timestampIn)
: phase(phaseIn), categoryName(categoryNameIn), name{}, timestamp(timestampIn)
: phase(phaseIn), categoryName(categoryNameIn), name{}, timestamp(timestampIn), tid(1)
{
ASSERT(strlen(nameIn) < kMaxNameLen);
strcpy(name, nameIn);
......
......@@ -51,6 +51,7 @@ struct TraceEvent final
const char *categoryName = nullptr;
char name[kMaxNameLen] = {};
double timestamp = 0;
uint32_t tid = 0;
};
class ANGLEPerfTest : public testing::Test, angle::NonCopyable
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment