Skip to content

Commit faab06b

Browse files
committed
Implement TimerQueries
1 parent 7d24234 commit faab06b

File tree

5 files changed

+152
-19
lines changed

5 files changed

+152
-19
lines changed

filament/backend/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,8 @@ if (FILAMENT_SUPPORTS_WEBGPU)
283283
src/webgpu/WebGPUSwapChain.h
284284
src/webgpu/WebGPUTexture.cpp
285285
src/webgpu/WebGPUTexture.h
286+
src/webgpu/WebGPUTimerQueries.cpp
287+
src/webgpu/WebGPUTimerQueries.h
286288
src/webgpu/WebGPUVertexBuffer.cpp
287289
src/webgpu/WebGPUVertexBuffer.h
288290
src/webgpu/WebGPUVertexBufferInfo.cpp

filament/backend/src/webgpu/WebGPUDriver.cpp

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "WebGPURenderTarget.h"
2626
#include "WebGPUSwapChain.h"
2727
#include "WebGPUTexture.h"
28+
#include "WebGPUTimerQueries.h"
2829
#include "WebGPUVertexBuffer.h"
2930
#include "WebGPUVertexBufferInfo.h"
3031
#include <backend/platforms/WebGPUPlatform.h>
@@ -216,9 +217,6 @@ void WebGPUDriver::destroyStream(Handle<HwStream> sh) {
216217
//TODO
217218
}
218219

219-
void WebGPUDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
220-
}
221-
222220
void WebGPUDriver::destroyDescriptorSetLayout(
223221
Handle<HwDescriptorSetLayout> descriptorSetLayoutHandle) {
224222
if (descriptorSetLayoutHandle) {
@@ -268,7 +266,29 @@ Handle<HwFence> WebGPUDriver::createFenceS() noexcept {
268266
}
269267

270268
Handle<HwTimerQuery> WebGPUDriver::createTimerQueryS() noexcept {
271-
return Handle<HwTimerQuery>((Handle<HwTimerQuery>::HandleId) mNextFakeHandle++);
269+
return allocAndConstructHandle<WebGPUTimerQueries, HwTimerQuery>();
270+
}
271+
272+
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> timerQueryHandle, int) {}
273+
274+
void WebGPUDriver::destroyTimerQuery(Handle<HwTimerQuery> timerQueryHandle) {
275+
if (timerQueryHandle) {
276+
destructHandle<WebGPUTimerQueries>(timerQueryHandle);
277+
}
278+
}
279+
280+
TimerQueryResult WebGPUDriver::getTimerQueryValue(Handle<HwTimerQuery> timerQueryHandle, uint64_t* elapsedTime) {
281+
auto* tq = handleCast<WebGPUTimerQueries>(timerQueryHandle);
282+
return tq->getQueryResult(elapsedTime) ? TimerQueryResult::AVAILABLE
283+
: TimerQueryResult::NOT_READY;
284+
}
285+
286+
void WebGPUDriver::beginTimerQuery(Handle<HwTimerQuery> timerQueryHandle) {
287+
mTimerQuery = handleCast<WebGPUTimerQueries>(timerQueryHandle);
288+
}
289+
290+
void WebGPUDriver::endTimerQuery(Handle<HwTimerQuery> timerQueryHandle) {
291+
mTimerQuery = handleCast<WebGPUTimerQueries>(timerQueryHandle);
272292
}
273293

274294
Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
@@ -462,8 +482,6 @@ void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {
462482
//todo
463483
}
464484

465-
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}
466-
467485
void WebGPUDriver::createDescriptorSetLayoutR(
468486
Handle<HwDescriptorSetLayout> descriptorSetLayoutHandle,
469487
backend::DescriptorSetLayout&& info) {
@@ -727,10 +745,6 @@ void WebGPUDriver::setupExternalImage(void* image) {
727745
//todo
728746
}
729747

730-
TimerQueryResult WebGPUDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
731-
return TimerQueryResult::ERROR;
732-
}
733-
734748
void WebGPUDriver::setupExternalImage2(Platform::ExternalImageHandleRef image) {
735749
//todo
736750
}
@@ -924,14 +938,20 @@ void WebGPUDriver::commit(Handle<HwSwapChain> sch) {
924938
mCommandBuffer = mCommandEncoder.Finish(&commandBufferDescriptor);
925939
assert_invariant(mCommandBuffer);
926940
mCommandEncoder = nullptr;
941+
mTimerQuery->beginTimeElapsedQuery();
927942
mQueue.Submit(1, &mCommandBuffer);
928-
929943
static bool firstRender = true;
930944
// For the first frame rendered, we need to make sure the work is done before presenting or we
931945
// get a purple flash
946+
auto f = mQueue.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly,
947+
[=](wgpu::QueueWorkDoneStatus status) {
948+
if (status == wgpu::QueueWorkDoneStatus::Success) {
949+
if (mTimerQuery) {
950+
mTimerQuery->endTimeElapsedQuery();
951+
}
952+
}
953+
});
932954
if (firstRender) {
933-
auto f = mQueue.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly,
934-
[=](wgpu::QueueWorkDoneStatus) {});
935955
const wgpu::Instance instance = mAdapter.GetInstance();
936956
auto wStatus = instance.WaitAny(f,
937957
std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count());
@@ -1151,12 +1171,6 @@ void WebGPUDriver::scissor(
11511171
//todo
11521172
}
11531173

1154-
void WebGPUDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
1155-
}
1156-
1157-
void WebGPUDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
1158-
}
1159-
11601174
void WebGPUDriver::resetState(int) {
11611175
//todo
11621176
}

filament/backend/src/webgpu/WebGPUDriver.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
namespace filament::backend {
4444

4545
class WebGPUSwapChain;
46+
class WebGPUTimerQueries;
4647

4748
/**
4849
* WebGPU backend (driver) implementation
@@ -78,6 +79,7 @@ class WebGPUDriver final : public DriverBase {
7879
WebGPURenderTarget* mDefaultRenderTarget = nullptr;
7980
WebGPURenderTarget* mCurrentRenderTarget = nullptr;
8081
spd::MipmapGenerator mMipMapGenerator;
82+
WebGPUTimerQueries* mTimerQuery = nullptr;
8183

8284
tsl::robin_map<uint32_t, wgpu::RenderPipeline> mPipelineMap;
8385

@@ -116,6 +118,11 @@ class WebGPUDriver final : public DriverBase {
116118
return mHandleAllocator.allocate<D>();
117119
}
118120

121+
template<typename D, typename B, typename... ARGS>
122+
Handle<B> allocAndConstructHandle(ARGS&&... args) {
123+
return mHandleAllocator.allocateAndConstruct<D>(std::forward<ARGS>(args)...);
124+
}
125+
119126
template<typename D, typename B, typename... ARGS>
120127
D* constructHandle(Handle<B>& handle, ARGS&&... args) noexcept {
121128
return mHandleAllocator.construct<D>(handle, std::forward<ARGS>(args)...);
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright (C) 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
18+
#include "WebGPUTimerQueries.h"
19+
20+
#include <chrono>
21+
#include <cstdint>
22+
#include <memory>
23+
24+
namespace filament::backend {
25+
26+
void WebGPUTimerQueries::beginTimeElapsedQuery() {
27+
status->elapsedNanoseconds = 0;
28+
// Capture the timer query status via a weak_ptr because the WGPUTimerQuery could be destroyed
29+
// before the block executes.
30+
std::weak_ptr<WebGPUTimerQueries::Status> statusPtr = status;
31+
32+
if (auto s = statusPtr.lock()) {
33+
s->elapsedNanoseconds = std::chrono::steady_clock::now().time_since_epoch().count();
34+
}
35+
}
36+
37+
void WebGPUTimerQueries::endTimeElapsedQuery() {
38+
// Capture the timer query status via a weak_ptr because the WGPUTimerQuery could be destroyed
39+
// before the block executes.
40+
if (status->elapsedNanoseconds != 0) {
41+
std::weak_ptr<WebGPUTimerQueries::Status> statusPtr = status;
42+
if (auto s = statusPtr.lock()) {
43+
s->previousElapsed = s->elapsedNanoseconds =
44+
std::chrono::steady_clock::now().time_since_epoch().count() -
45+
s->elapsedNanoseconds;
46+
}
47+
}
48+
}
49+
50+
bool WebGPUTimerQueries::getQueryResult(uint64_t* outElapsedTime) {
51+
if (status->previousElapsed == 0) {
52+
return false;
53+
}
54+
if (outElapsedTime) {
55+
*outElapsedTime = status->previousElapsed;
56+
status->previousElapsed = 0;
57+
}
58+
return true;
59+
}
60+
61+
}// namespace filament::backend
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (C) 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef TNT_FILAMENT_BACKEND_WEBGPUTIMERQUERIES_H
18+
#define TNT_FILAMENT_BACKEND_WEBGPUTIMERQUERIES_H
19+
20+
#include <webgpu/webgpu_cpp.h>
21+
22+
#include "DriverBase.h"
23+
#include <backend/Platform.h>
24+
25+
#include <cstdint>
26+
27+
namespace filament::backend {
28+
29+
class WebGPUTimerQueries : public HwTimerQuery {
30+
public:
31+
WebGPUTimerQueries()
32+
: status(std::make_shared<Status>()) {}
33+
34+
void beginTimeElapsedQuery();
35+
void endTimeElapsedQuery();
36+
bool getQueryResult(uint64_t* outElapsedTimeNanoseconds);
37+
38+
private:
39+
struct Status {
40+
std::atomic<uint64_t> elapsedNanoseconds{ 0 };
41+
std::atomic<uint64_t> previousElapsed{ 0 };
42+
};
43+
44+
std::shared_ptr<Status> status;
45+
};
46+
47+
} // namespace filament::backend
48+
49+
#endif //TNT_FILAMENT_BACKEND_WEBGPUTIMERQUERIES_H

0 commit comments

Comments
 (0)