Skip to content

Commit

Permalink
Vulkan: Debug overlay
Browse files Browse the repository at this point in the history
A debug overlay system for the Vulkan backend designed with efficiency
and runtime configurability in mind.  Overlay widgets are of two
fundamental types:

- Text widgets: A single line of text with small, medium or large font.
- Graph widgets: A bar graph of data.

Built on these, various overlay widget types are defined that gather
statistics.  Five such types are defined with one widget per type as
example:

- Count: A widget that counts something.  VulkanValidationMessageCount
  is an overlay widget of this type that shows the number of validation
  messages received from the validation layers.
- Text: A generic text.  VulkanLastValidationMessage is an overlay
  widget of this type that shows the last validation message.
- PerSecond: A value that gets reset every second automatically.  FPS is
  an overlay widget of this type that simply gets incremented on every
  swap().
- RunningGraph: A graph of last N values.  VulkanCommandGraphSize is an
  overlay of this type.  On every vkQueueSubmit, the number of nodes in
  the command graph is accumulated.  On every present(), the value is
  taken as the number of nodes for the whole duration of the frame.
- RunningHistogram: A histogram of last N values.  Input values are in
  the [0, 1] range and they are ranked to N buckets for histogram
  calculation.  VulkanSecondaryCommandBufferPoolWaste is an overlay
  widget of this type.  On vkQueueSubmit, the memory waste from command
  buffer pool allocations is recorded in the histogram.

Overlay font is placed in libANGLE/overlay/ which gen_overlay_fonts.py
processes to create an array of bits, which is processed at runtime to
create the actual font image (an image with 3 layers).

The overlay widget layout is defined in overlay_widgets.json which
gen_overlay_widgets.py processes to generate an array of widgetss, each
of its respective type, and sets their properties, such as color and
bounding box.  The json file allows widgets to align against other
widgets as well as against the framebuffer edges.

Two compute shaders are implemented to efficiently render the UI:

- OverlayCull: This shader creates a bitset of Text and Graph widgets
  whose bounding boxes intersect a corresponding subgroup processed by
  OverlayDraw.  This is done only when the enabled overlay widgets are
  changed (a feature that is not yet implemented) or the surface is
  resized.
- OverlayDraw: Using the bitsets generated by OverlayCull, values that
  are uniform for each workgroup (set to be equal to hardware subgroup
  size), this shader loops over enabled widgets that can possibly
  intersect the pixel being processed and renders and blends in texts
  and graphs.  This is done once per frame on present().

Currently, to enable overlay widgets an environment variable is used.
For example:

    $ export ANGLE_OVERLAY=FPS:VulkanSecondaryCommandBufferPoolWaste
    $ ./hello_triangle --use-angle=vulkan

Possible future work:

- On Android, add settings in developer options and enable widgets based
  on those.
- Spawn a small server in ANGLE and write an application that sends
  enable/disable commands remotely.
- Implement overlay for other backends.

Bug: angleproject:3757
Change-Id: If9c6974d1935c18f460ec569e79b41188bd7afcc
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1729440
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
  • Loading branch information
ShabbyX authored and Commit Bot committed Aug 31, 2019
1 parent c3f5723 commit e54d0f9
Show file tree
Hide file tree
Showing 70 changed files with 8,253 additions and 43 deletions.
4 changes: 4 additions & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,10 @@ angle_source_set("libANGLE_base") {
defines += [ "ANGLE_GENERATE_SHADER_DEBUG_INFO" ]
}

if (angle_enable_overlay) {
defines += [ "ANGLE_ENABLE_OVERLAY=1" ]
}

configs += [ ":debug_annotations_config" ]
public_configs += [
":libANGLE_config",
Expand Down
3 changes: 3 additions & 0 deletions gni/angle.gni
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ declare_args() {
# Enable custom (cpu-side) secondary command buffers
angle_enable_custom_vulkan_cmd_buffers = true
}

# Disable overlay by default
angle_enable_overlay = false
}

if (is_win) {
Expand Down
2 changes: 1 addition & 1 deletion samples/sample_util/SampleApplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ SampleApplication::SampleApplication(std::string name,
angle::OpenSharedLibrary(ANGLE_EGL_LIBRARY_NAME, angle::SearchType::ApplicationDir));

mEGLWindow = EGLWindow::New(glesMajorVersion, glesMinorVersion);
mOSWindow = OSWindow::New();
mOSWindow = OSWindow::New();
}

SampleApplication::~SampleApplication()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"src/libANGLE/renderer/vulkan/gen_vk_internal_shaders.py":
"76bc7ed183ce82e687b16f0496cc9f09",
"b05fa7c291a7c05d9bb1dd075990275b",
"src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000000.inc":
"31832c377e532cd5ea05aab57154b8f8",
"src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000001.inc":
Expand Down Expand Up @@ -223,6 +223,22 @@
"5bb2d9e8ee68f8ec2dacd4a056f8eff2",
"src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000015.inc":
"8152303c7825ff73d9972d95520852dd",
"src/libANGLE/renderer/vulkan/shaders/gen/OverlayCull.comp.00000000.inc":
"1dc870604216818c7ca757ebb7cd22bc",
"src/libANGLE/renderer/vulkan/shaders/gen/OverlayCull.comp.00000001.inc":
"cb05ee0936a2235f49994497bd831b20",
"src/libANGLE/renderer/vulkan/shaders/gen/OverlayCull.comp.00000002.inc":
"9c6973f0e4035a411de5425d48a8ded5",
"src/libANGLE/renderer/vulkan/shaders/gen/OverlayCull.comp.00000003.inc":
"93a50e9365cef6be89033df687db44ad",
"src/libANGLE/renderer/vulkan/shaders/gen/OverlayCull.comp.00000004.inc":
"78e697d35c094a750a6cb6689b9d35c4",
"src/libANGLE/renderer/vulkan/shaders/gen/OverlayCull.comp.00000005.inc":
"5c57c4cb2507aee3e7d0d53555ef7471",
"src/libANGLE/renderer/vulkan/shaders/gen/OverlayDraw.comp.00000000.inc":
"a341905ac08458eab61199817fe40c7e",
"src/libANGLE/renderer/vulkan/shaders/gen/OverlayDraw.comp.00000001.inc":
"25ba613bf71f7c1397bfe2d089d57ac6",
"src/libANGLE/renderer/vulkan/shaders/src/BlitResolve.frag":
"a3ecba7bc86093f90b183605bed82813",
"src/libANGLE/renderer/vulkan/shaders/src/BlitResolveStencilNoExport.comp":
Expand All @@ -239,10 +255,14 @@
"8889ae8014a657a0efd5607954126945",
"src/libANGLE/renderer/vulkan/shaders/src/ImageCopy.frag":
"f0f3cc82d78198f114b698e1aea31267",
"src/libANGLE/renderer/vulkan/shaders/src/OverlayCull.comp":
"c89a0d185f7723e0c221c135aa4f48a3",
"src/libANGLE/renderer/vulkan/shaders/src/OverlayDraw.comp":
"dcc246b398b2e07a869a264666499362",
"src/libANGLE/renderer/vulkan/vk_internal_shaders_autogen.cpp":
"0660e11229f28464a6de11eb07fff7d3",
"9035e5a2674bbab577576d5b704fbe99",
"src/libANGLE/renderer/vulkan/vk_internal_shaders_autogen.h":
"0532a7219ffa0a5a9ca0cd7a6eb3206e",
"bd3beed76c9069d967cc2946a035e2cc",
"tools/glslang/glslang_validator.exe.sha1":
"289f30598865a987a21b79ae525fc66f",
"tools/glslang/glslang_validator.sha1":
Expand Down
10 changes: 10 additions & 0 deletions scripts/code_generation_hashes/overlay_fonts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"src/libANGLE/Overlay_font_autogen.cpp":
"ff9bf191978e8c9cff6de3e31f3664f2",
"src/libANGLE/Overlay_font_autogen.h":
"2eae247cebb716c4f591de8a251b4a8e",
"src/libANGLE/gen_overlay_fonts.py":
"e257b626441bf057856eb357a1a2ca5c",
"src/libANGLE/overlay/DejaVuSansMono-Bold.ttf":
"f580ed7569a1967ceeb96fa3e1b234d4"
}
8 changes: 8 additions & 0 deletions scripts/code_generation_hashes/overlay_widgets.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"src/libANGLE/Overlay_autogen.cpp":
"514b8108f62ef616c296dc511bb2f644",
"src/libANGLE/gen_overlay_widgets.py":
"07252fbde304fd48559ae07f8f920a08",
"src/libANGLE/overlay_widgets.json":
"552b1e2883a12c38d427c7fbd1c2bf22"
}
4 changes: 4 additions & 0 deletions scripts/run_code_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ def auto_script(script):
'src/libANGLE/renderer/vulkan/gen_vk_mandatory_format_support_table.py',
'Vulkan internal shader programs':
'src/libANGLE/renderer/vulkan/gen_vk_internal_shaders.py',
'overlay fonts':
'src/libANGLE/gen_overlay_fonts.py',
'overlay widgets':
'src/libANGLE/gen_overlay_widgets.py',
'Emulated HLSL functions':
'src/compiler/translator/gen_emulated_builtin_function_tables.py',
'Static builtins':
Expand Down
9 changes: 8 additions & 1 deletion src/libANGLE/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ Context::Context(egl::Display *display,
: mState(reinterpret_cast<ContextID>(this),
shareContext ? &shareContext->mState : nullptr,
shareTextures,
&mOverlay,
clientType,
GetClientVersion(display, attribs),
GetDebug(attribs),
Expand Down Expand Up @@ -352,7 +353,8 @@ Context::Context(egl::Display *display,
mScratchBuffer(1000u),
mZeroFilledBuffer(1000u),
mThreadPool(nullptr),
mFrameCapture(new angle::FrameCapture)
mFrameCapture(new angle::FrameCapture),
mOverlay(mImplementation.get())
{
for (angle::SubjectIndex uboIndex = kUniformBuffer0SubjectIndex;
uboIndex < kUniformBufferMaxSubjectIndex; ++uboIndex)
Expand Down Expand Up @@ -539,6 +541,9 @@ void Context::initialize()
mCopyImageDirtyObjects.set(State::DIRTY_OBJECT_READ_FRAMEBUFFER);

ANGLE_CONTEXT_TRY(mImplementation->initialize());

// Initialize overlay after implementation is initialized.
ANGLE_CONTEXT_TRY(mOverlay.init(this));
}

egl::Error Context::onDestroy(const egl::Display *display)
Expand Down Expand Up @@ -611,6 +616,8 @@ egl::Error Context::onDestroy(const egl::Display *display)

mImplementation->onDestroy(this);

mOverlay.destroy(this);

return egl::NoError();
}

Expand Down
2 changes: 2 additions & 0 deletions src/libANGLE/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,8 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl

// Note: we use a raw pointer here so we can exclude frame capture sources from the build.
std::unique_ptr<angle::FrameCapture> mFrameCapture;

OverlayType mOverlay;
};

} // namespace gl
Expand Down
109 changes: 109 additions & 0 deletions src/libANGLE/Overlay.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Overlay.cpp:
// Implements the Overlay class.
//

#include "libANGLE/Overlay.h"

#include "common/system_utils.h"
#include "libANGLE/Context.h"
#include "libANGLE/Overlay_font_autogen.h"
#include "libANGLE/renderer/GLImplFactory.h"
#include "libANGLE/renderer/OverlayImpl.h"

#include <numeric>

namespace gl
{
namespace
{
constexpr std::pair<const char *, WidgetId> kWidgetNames[] = {
{"FPS", WidgetId::FPS},
{"VulkanLastValidationMessage", WidgetId::VulkanLastValidationMessage},
{"VulkanValidationMessageCount", WidgetId::VulkanValidationMessageCount},
{"VulkanCommandGraphSize", WidgetId::VulkanCommandGraphSize},
{"VulkanSecondaryCommandBufferPoolWaste", WidgetId::VulkanSecondaryCommandBufferPoolWaste},
};
} // namespace

OverlayState::OverlayState() : mEnabledWidgetCount(0), mOverlayWidgets{} {}
OverlayState::~OverlayState() = default;

Overlay::Overlay(rx::GLImplFactory *factory)
: mLastPerSecondUpdate(0), mImplementation(factory->createOverlay(mState))
{}
Overlay::~Overlay() = default;

angle::Result Overlay::init(const Context *context)
{
initOverlayWidgets();
mLastPerSecondUpdate = angle::GetCurrentTime();

ASSERT(std::all_of(
mState.mOverlayWidgets.begin(), mState.mOverlayWidgets.end(),
[](const std::unique_ptr<overlay::Widget> &widget) { return widget.get() != nullptr; }));

enableOverlayWidgetsFromEnvironment();

return mImplementation->init(context);
}

void Overlay::destroy(const gl::Context *context)
{
ASSERT(mImplementation);
mImplementation->onDestroy(context);
}

void Overlay::enableOverlayWidgetsFromEnvironment()
{
std::istringstream angleOverlayWidgets(angle::GetEnvironmentVar("ANGLE_OVERLAY"));

std::set<std::string> enabledWidgets;
std::string widget;
while (getline(angleOverlayWidgets, widget, ':'))
{
enabledWidgets.insert(widget);
}

for (const std::pair<const char *, WidgetId> &widgetName : kWidgetNames)
{
if (enabledWidgets.count(widgetName.first) > 0)
{
mState.mOverlayWidgets[widgetName.second]->enabled = true;
++mState.mEnabledWidgetCount;
}
}
}

void Overlay::onSwap() const
{
// Increment FPS counter.
getPerSecondWidget(WidgetId::FPS)->add(1);

// Update per second values every second.
double currentTime = angle::GetCurrentTime();
double timeDiff = currentTime - mLastPerSecondUpdate;
if (timeDiff >= 1.0)
{
for (const std::unique_ptr<overlay::Widget> &widget : mState.mOverlayWidgets)
{
if (widget->type == WidgetType::PerSecond)
{
overlay::PerSecond *perSecond =
reinterpret_cast<overlay::PerSecond *>(widget.get());
perSecond->lastPerSecondCount = static_cast<size_t>(perSecond->count / timeDiff);
perSecond->count = 0;
}
}
mLastPerSecondUpdate += 1.0;
}
}

DummyOverlay::DummyOverlay(rx::GLImplFactory *implFactory) {}
DummyOverlay::~DummyOverlay() = default;

} // namespace gl
Loading

0 comments on commit e54d0f9

Please sign in to comment.