1 // Copyright 2019 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "tests/perf_tests/DawnPerfTestPlatform.h"
16
17 #include <algorithm>
18
19 #include "common/Assert.h"
20 #include "common/HashUtils.h"
21 #include "dawn_platform/tracing/TraceEvent.h"
22 #include "tests/perf_tests/DawnPerfTest.h"
23 #include "utils/Timer.h"
24 namespace {
25
26 struct TraceCategoryInfo {
27 unsigned char enabled;
28 dawn_platform::TraceCategory category;
29 };
30
31 constexpr TraceCategoryInfo gTraceCategories[4] = {
32 {1, dawn_platform::TraceCategory::General},
33 {1, dawn_platform::TraceCategory::Validation},
34 {1, dawn_platform::TraceCategory::Recording},
35 {1, dawn_platform::TraceCategory::GPUWork},
36 };
37
38 static_assert(static_cast<uint32_t>(dawn_platform::TraceCategory::General) == 0, "");
39 static_assert(static_cast<uint32_t>(dawn_platform::TraceCategory::Validation) == 1, "");
40 static_assert(static_cast<uint32_t>(dawn_platform::TraceCategory::Recording) == 2, "");
41 static_assert(static_cast<uint32_t>(dawn_platform::TraceCategory::GPUWork) == 3, "");
42
43 } // anonymous namespace
44
DawnPerfTestPlatform()45 DawnPerfTestPlatform::DawnPerfTestPlatform()
46 : dawn_platform::Platform(), mTimer(utils::CreateTimer()) {
47 }
48
49 DawnPerfTestPlatform::~DawnPerfTestPlatform() = default;
50
GetTraceCategoryEnabledFlag(dawn_platform::TraceCategory category)51 const unsigned char* DawnPerfTestPlatform::GetTraceCategoryEnabledFlag(
52 dawn_platform::TraceCategory category) {
53 switch (category) {
54 case dawn_platform::TraceCategory::General:
55 case dawn_platform::TraceCategory::Validation:
56 case dawn_platform::TraceCategory::Recording:
57 case dawn_platform::TraceCategory::GPUWork:
58 break;
59 default:
60 UNREACHABLE();
61 }
62 return &gTraceCategories[static_cast<uint32_t>(category)].enabled;
63 }
64
MonotonicallyIncreasingTime()65 double DawnPerfTestPlatform::MonotonicallyIncreasingTime() {
66 // Move the time origin to the first call to this function, to avoid generating
67 // unnecessarily large timestamps.
68 static double origin = mTimer->GetAbsoluteTime();
69 return mTimer->GetAbsoluteTime() - origin;
70 }
71
GetLocalTraceEventBuffer()72 std::vector<DawnPerfTestPlatform::TraceEvent>* DawnPerfTestPlatform::GetLocalTraceEventBuffer() {
73 // Cache the pointer to the vector in thread_local storage
74 thread_local std::vector<TraceEvent>* traceEventBuffer = nullptr;
75
76 if (traceEventBuffer == nullptr) {
77 auto buffer = std::make_unique<std::vector<TraceEvent>>();
78 traceEventBuffer = buffer.get();
79
80 // Add a new buffer to the map
81 std::lock_guard<std::mutex> guard(mTraceEventBufferMapMutex);
82 mTraceEventBuffers[std::this_thread::get_id()] = std::move(buffer);
83 }
84
85 return traceEventBuffer;
86 }
87
88 // TODO(enga): Simplify this API.
AddTraceEvent(char phase,const unsigned char * categoryGroupEnabled,const char * name,uint64_t id,double timestamp,int numArgs,const char ** argNames,const unsigned char * argTypes,const uint64_t * argValues,unsigned char flags)89 uint64_t DawnPerfTestPlatform::AddTraceEvent(char phase,
90 const unsigned char* categoryGroupEnabled,
91 const char* name,
92 uint64_t id,
93 double timestamp,
94 int numArgs,
95 const char** argNames,
96 const unsigned char* argTypes,
97 const uint64_t* argValues,
98 unsigned char flags) {
99 if (!mRecordTraceEvents) {
100 return 0;
101 }
102
103 // Discover the category name based on categoryGroupEnabled. This flag comes from the first
104 // parameter of TraceCategory, and corresponds to one of the entries in gTraceCategories.
105 static_assert(offsetof(TraceCategoryInfo, enabled) == 0,
106 "|enabled| must be the first field of the TraceCategoryInfo class.");
107
108 const TraceCategoryInfo* info =
109 reinterpret_cast<const TraceCategoryInfo*>(categoryGroupEnabled);
110
111 std::vector<TraceEvent>* buffer = GetLocalTraceEventBuffer();
112 buffer->emplace_back(phase, info->category, name, id, timestamp);
113
114 size_t hash = 0;
115 HashCombine(&hash, buffer->size());
116 HashCombine(&hash, std::this_thread::get_id());
117 return static_cast<uint64_t>(hash);
118 }
119
EnableTraceEventRecording(bool enable)120 void DawnPerfTestPlatform::EnableTraceEventRecording(bool enable) {
121 mRecordTraceEvents = enable;
122 }
123
AcquireTraceEventBuffer()124 std::vector<DawnPerfTestPlatform::TraceEvent> DawnPerfTestPlatform::AcquireTraceEventBuffer() {
125 std::vector<TraceEvent> traceEventBuffer;
126 {
127 // AcquireTraceEventBuffer should only be called when Dawn is completely idle. There should
128 // be no threads inserting trace events.
129 // Right now, this is safe because AcquireTraceEventBuffer is called after waiting on a
130 // fence for all GPU commands to finish executing. When Dawn has multiple background threads
131 // for other work (creation, validation, submission, residency, etc), we will need to ensure
132 // all work on those threads is stopped as well.
133 std::lock_guard<std::mutex> guard(mTraceEventBufferMapMutex);
134 for (auto it = mTraceEventBuffers.begin(); it != mTraceEventBuffers.end(); ++it) {
135 std::ostringstream stream;
136 stream << it->first;
137 std::string threadId = stream.str();
138
139 std::transform(it->second->begin(), it->second->end(),
140 std::back_inserter(traceEventBuffer), [&threadId](TraceEvent ev) {
141 ev.threadId = threadId;
142 return ev;
143 });
144 it->second->clear();
145 }
146 }
147 return traceEventBuffer;
148 }
149