Skip to content

Commit 04ff88f

Browse files
committed
Implement TimerQueries
1 parent 7d24234 commit 04ff88f

File tree

5 files changed

+157
-25
lines changed

5 files changed

+157
-25
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/WebGPUTimerQuery.cpp
287+
src/webgpu/WebGPUTimerQuery.h
286288
src/webgpu/WebGPUVertexBuffer.cpp
287289
src/webgpu/WebGPUVertexBuffer.h
288290
src/webgpu/WebGPUVertexBufferInfo.cpp

filament/backend/src/webgpu/WebGPUDriver.cpp

Lines changed: 38 additions & 25 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 "WebGPUTimerQuery.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,31 @@ Handle<HwFence> WebGPUDriver::createFenceS() noexcept {
268266
}
269267

270268
Handle<HwTimerQuery> WebGPUDriver::createTimerQueryS() noexcept {
271-
return Handle<HwTimerQuery>((Handle<HwTimerQuery>::HandleId) mNextFakeHandle++);
269+
return allocAndConstructHandle<WebGPUTimerQuery, HwTimerQuery>();
270+
}
271+
272+
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> timerQueryHandle, int /*dummy*/) {
273+
// nothing to do, timer query was constructed in createTimerQueryS
274+
}
275+
276+
void WebGPUDriver::destroyTimerQuery(Handle<HwTimerQuery> timerQueryHandle) {
277+
if (timerQueryHandle) {
278+
destructHandle<WebGPUTimerQuery>(timerQueryHandle);
279+
}
280+
}
281+
282+
TimerQueryResult WebGPUDriver::getTimerQueryValue(Handle<HwTimerQuery> timerQueryHandle, uint64_t* elapsedTime) {
283+
auto* timerQuery = handleCast<WebGPUTimerQuery>(timerQueryHandle);
284+
return timerQuery->getQueryResult(elapsedTime) ? TimerQueryResult::AVAILABLE
285+
: TimerQueryResult::NOT_READY;
286+
}
287+
288+
void WebGPUDriver::beginTimerQuery(Handle<HwTimerQuery> timerQueryHandle) {
289+
mTimerQuery = handleCast<WebGPUTimerQuery>(timerQueryHandle);
290+
}
291+
292+
void WebGPUDriver::endTimerQuery(Handle<HwTimerQuery> timerQueryHandle) {
293+
mTimerQuery = handleCast<WebGPUTimerQuery>(timerQueryHandle);
272294
}
273295

274296
Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
@@ -462,8 +484,6 @@ void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {
462484
//todo
463485
}
464486

465-
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}
466-
467487
void WebGPUDriver::createDescriptorSetLayoutR(
468488
Handle<HwDescriptorSetLayout> descriptorSetLayoutHandle,
469489
backend::DescriptorSetLayout&& info) {
@@ -727,10 +747,6 @@ void WebGPUDriver::setupExternalImage(void* image) {
727747
//todo
728748
}
729749

730-
TimerQueryResult WebGPUDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
731-
return TimerQueryResult::ERROR;
732-
}
733-
734750
void WebGPUDriver::setupExternalImage2(Platform::ExternalImageHandleRef image) {
735751
//todo
736752
}
@@ -924,23 +940,26 @@ void WebGPUDriver::commit(Handle<HwSwapChain> sch) {
924940
mCommandBuffer = mCommandEncoder.Finish(&commandBufferDescriptor);
925941
assert_invariant(mCommandBuffer);
926942
mCommandEncoder = nullptr;
943+
if (mTimerQuery) {
944+
mTimerQuery->beginTimeElapsedQuery();
945+
}
927946
mQueue.Submit(1, &mCommandBuffer);
928-
929-
static bool firstRender = true;
930-
// For the first frame rendered, we need to make sure the work is done before presenting or we
931-
// get a purple flash
932-
if (firstRender) {
933-
auto f = mQueue.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly,
934-
[=](wgpu::QueueWorkDoneStatus) {});
947+
auto f = mQueue.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly,
948+
[=](wgpu::QueueWorkDoneStatus status) {
949+
if (status == wgpu::QueueWorkDoneStatus::Success) {
950+
if (mTimerQuery) {
951+
mTimerQuery->endTimeElapsedQuery();
952+
}
953+
}
954+
});
935955
const wgpu::Instance instance = mAdapter.GetInstance();
936956
auto wStatus = instance.WaitAny(f,
937957
std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count());
938958
if (wStatus != wgpu::WaitStatus::Success) {
939959
FWGPU_LOGW << "Waiting for first frame work to finish resulted in an error"
940960
<< static_cast<uint32_t>(wStatus);
941961
}
942-
firstRender = false;
943-
}
962+
944963
mCommandBuffer = nullptr;
945964
mTextureView = nullptr;
946965
assert_invariant(mSwapChain);
@@ -1151,12 +1170,6 @@ void WebGPUDriver::scissor(
11511170
//todo
11521171
}
11531172

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

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 WebGPUTimerQuery;
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+
WebGPUTimerQuery* 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: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
#include "WebGPUTimerQuery.h"
18+
19+
#include <chrono>
20+
#include <cstdint>
21+
#include <memory>
22+
23+
namespace filament::backend {
24+
25+
void WebGPUTimerQuery::beginTimeElapsedQuery() {
26+
mStatus->elapsedNanoseconds = 0;
27+
// Capture the timer query status via a weak_ptr because the WGPUTimerQuery could be destroyed
28+
// before the block executes.
29+
std::weak_ptr<WebGPUTimerQuery::Status> statusPtr = mStatus;
30+
31+
if (auto s = statusPtr.lock()) {
32+
s->elapsedNanoseconds = std::chrono::steady_clock::now().time_since_epoch().count();
33+
}
34+
}
35+
36+
void WebGPUTimerQuery::endTimeElapsedQuery() {
37+
// Capture the timer query status via a weak_ptr because the WGPUTimerQuery could be destroyed
38+
// before the block executes.
39+
if (mStatus->elapsedNanoseconds != 0) {
40+
std::weak_ptr<WebGPUTimerQuery::Status> statusPtr = mStatus;
41+
if (auto s = statusPtr.lock()) {
42+
s->previousElapsed = s->elapsedNanoseconds =
43+
std::chrono::steady_clock::now().time_since_epoch().count() -
44+
s->elapsedNanoseconds;
45+
}
46+
}
47+
}
48+
49+
bool WebGPUTimerQuery::getQueryResult(uint64_t* outElapsedTime) {
50+
if (mStatus->previousElapsed == 0) {
51+
return false;
52+
}
53+
if (outElapsedTime) {
54+
*outElapsedTime = mStatus->previousElapsed;
55+
mStatus->previousElapsed = 0;
56+
}
57+
return true;
58+
}
59+
60+
}// namespace filament::backend
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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_WEBGPUTIMERQUERY_H
18+
#define TNT_FILAMENT_BACKEND_WEBGPUTIMERQUERY_H
19+
20+
#include "DriverBase.h"
21+
22+
#include <webgpu/webgpu_cpp.h>
23+
24+
#include <atomic>
25+
#include <cstdint>
26+
#include <memory>
27+
28+
namespace filament::backend {
29+
30+
class WebGPUTimerQuery : public HwTimerQuery {
31+
public:
32+
WebGPUTimerQuery()
33+
: mStatus(std::make_shared<Status>()) {}
34+
35+
void beginTimeElapsedQuery();
36+
void endTimeElapsedQuery();
37+
bool getQueryResult(uint64_t* outElapsedTimeNanoseconds);
38+
39+
private:
40+
struct Status {
41+
std::atomic<uint64_t> elapsedNanoseconds{ 0 };
42+
std::atomic<uint64_t> previousElapsed{ 0 };
43+
};
44+
45+
std::shared_ptr<Status> mStatus;
46+
};
47+
48+
} // namespace filament::backend
49+
50+
#endif //TNT_FILAMENT_BACKEND_WEBGPUTIMERQUERY_H

0 commit comments

Comments
 (0)