-
Notifications
You must be signed in to change notification settings - Fork 2k
webgpu: Implement TimerQueries #8880
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,7 @@ | |
#include "WebGPURenderTarget.h" | ||
#include "WebGPUSwapChain.h" | ||
#include "WebGPUTexture.h" | ||
#include "WebGPUTimerQuery.h" | ||
#include "WebGPUVertexBuffer.h" | ||
#include "WebGPUVertexBufferInfo.h" | ||
#include <backend/platforms/WebGPUPlatform.h> | ||
|
@@ -101,8 +102,9 @@ template class ConcreteDispatcher<WebGPUDriver>; | |
void WebGPUDriver::terminate() { | ||
} | ||
|
||
void WebGPUDriver::tick(int) { | ||
void WebGPUDriver::tick(int /*dummy*/) { | ||
mDevice.Tick(); | ||
mAdapter.GetInstance().ProcessEvents(); | ||
} | ||
|
||
void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns, | ||
|
@@ -216,9 +218,6 @@ void WebGPUDriver::destroyStream(Handle<HwStream> sh) { | |
//TODO | ||
} | ||
|
||
void WebGPUDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) { | ||
} | ||
|
||
void WebGPUDriver::destroyDescriptorSetLayout( | ||
Handle<HwDescriptorSetLayout> descriptorSetLayoutHandle) { | ||
if (descriptorSetLayoutHandle) { | ||
|
@@ -268,7 +267,31 @@ Handle<HwFence> WebGPUDriver::createFenceS() noexcept { | |
} | ||
|
||
Handle<HwTimerQuery> WebGPUDriver::createTimerQueryS() noexcept { | ||
return Handle<HwTimerQuery>((Handle<HwTimerQuery>::HandleId) mNextFakeHandle++); | ||
return allocAndConstructHandle<WebGPUTimerQuery, HwTimerQuery>(); | ||
} | ||
|
||
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> timerQueryHandle, int /*dummy*/) { | ||
// nothing to do, timer query was constructed in createTimerQueryS | ||
} | ||
|
||
void WebGPUDriver::destroyTimerQuery(Handle<HwTimerQuery> timerQueryHandle) { | ||
if (timerQueryHandle) { | ||
destructHandle<WebGPUTimerQuery>(timerQueryHandle); | ||
} | ||
} | ||
|
||
TimerQueryResult WebGPUDriver::getTimerQueryValue(Handle<HwTimerQuery> timerQueryHandle, uint64_t* elapsedTime) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: rename There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We routinely use slightly different names for added clarity. For example, in the same signature we use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: I would make the pointer |
||
auto* timerQuery = handleCast<WebGPUTimerQuery>(timerQueryHandle); | ||
return timerQuery->getQueryResult(elapsedTime) ? TimerQueryResult::AVAILABLE | ||
: TimerQueryResult::NOT_READY; | ||
} | ||
|
||
void WebGPUDriver::beginTimerQuery(Handle<HwTimerQuery> timerQueryHandle) { | ||
mTimerQuery = handleCast<WebGPUTimerQuery>(timerQueryHandle); | ||
} | ||
|
||
void WebGPUDriver::endTimerQuery(Handle<HwTimerQuery> timerQueryHandle) { | ||
mTimerQuery = handleCast<WebGPUTimerQuery>(timerQueryHandle); | ||
} | ||
|
||
Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept { | ||
|
@@ -462,8 +485,6 @@ void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) { | |
//todo | ||
} | ||
|
||
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {} | ||
|
||
void WebGPUDriver::createDescriptorSetLayoutR( | ||
Handle<HwDescriptorSetLayout> descriptorSetLayoutHandle, | ||
backend::DescriptorSetLayout&& info) { | ||
|
@@ -727,10 +748,6 @@ void WebGPUDriver::setupExternalImage(void* image) { | |
//todo | ||
} | ||
|
||
TimerQueryResult WebGPUDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) { | ||
return TimerQueryResult::ERROR; | ||
} | ||
|
||
void WebGPUDriver::setupExternalImage2(Platform::ExternalImageHandleRef image) { | ||
//todo | ||
} | ||
|
@@ -924,8 +941,10 @@ void WebGPUDriver::commit(Handle<HwSwapChain> sch) { | |
mCommandBuffer = mCommandEncoder.Finish(&commandBufferDescriptor); | ||
assert_invariant(mCommandBuffer); | ||
mCommandEncoder = nullptr; | ||
if (mTimerQuery) { | ||
mTimerQuery->beginTimeElapsedQuery(); | ||
} | ||
mQueue.Submit(1, &mCommandBuffer); | ||
|
||
static bool firstRender = true; | ||
// For the first frame rendered, we need to make sure the work is done before presenting or we | ||
// get a purple flash | ||
|
@@ -940,6 +959,15 @@ void WebGPUDriver::commit(Handle<HwSwapChain> sch) { | |
<< static_cast<uint32_t>(wStatus); | ||
} | ||
firstRender = false; | ||
} else { | ||
mQueue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowSpontaneous, | ||
[=](wgpu::QueueWorkDoneStatus status) { | ||
if (status == wgpu::QueueWorkDoneStatus::Success) { | ||
if (mTimerQuery) { | ||
mTimerQuery->endTimeElapsedQuery(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Per our conversation offline, I think this may not be accurate enough for our purposes, as it depends on when we call ProcessEvents (the driver's tick call). Unfortunately, I think we may need to take a different approach of adding up render/compute pass times. |
||
} | ||
} | ||
}); | ||
} | ||
mCommandBuffer = nullptr; | ||
mTextureView = nullptr; | ||
|
@@ -1151,12 +1179,6 @@ void WebGPUDriver::scissor( | |
//todo | ||
} | ||
|
||
void WebGPUDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) { | ||
} | ||
|
||
void WebGPUDriver::endTimerQuery(Handle<HwTimerQuery> tqh) { | ||
} | ||
|
||
void WebGPUDriver::resetState(int) { | ||
//todo | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/* | ||
* Copyright (C) 2025 The Android Open Source Project | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#include "WebGPUTimerQuery.h" | ||
|
||
#include <chrono> | ||
#include <cstdint> | ||
#include <memory> | ||
|
||
namespace filament::backend { | ||
|
||
void WebGPUTimerQuery::beginTimeElapsedQuery() { | ||
mStatus->elapsedNanoseconds = 0; | ||
// Capture the timer query status via a weak_ptr because the WGPUTimerQuery could be destroyed | ||
// before the block executes. | ||
std::weak_ptr<WebGPUTimerQuery::Status> statusPtr = mStatus; | ||
|
||
if (auto s = statusPtr.lock()) { | ||
s->elapsedNanoseconds = std::chrono::steady_clock::now().time_since_epoch().count(); | ||
} | ||
} | ||
|
||
void WebGPUTimerQuery::endTimeElapsedQuery() { | ||
// Capture the timer query status via a weak_ptr because the WGPUTimerQuery could be destroyed | ||
// before the block executes. | ||
if (mStatus->elapsedNanoseconds != 0) { | ||
std::weak_ptr<WebGPUTimerQuery::Status> statusPtr = mStatus; | ||
if (auto s = statusPtr.lock()) { | ||
s->previousElapsed = s->elapsedNanoseconds = | ||
std::chrono::steady_clock::now().time_since_epoch().count() - | ||
s->elapsedNanoseconds; | ||
} | ||
} | ||
} | ||
|
||
bool WebGPUTimerQuery::getQueryResult(uint64_t* outElapsedTime) { | ||
if (mStatus->previousElapsed == 0) { | ||
return false; | ||
} | ||
if (outElapsedTime) { | ||
*outElapsedTime = mStatus->previousElapsed; | ||
mStatus->previousElapsed = 0; | ||
} | ||
return true; | ||
} | ||
|
||
}// namespace filament::backend |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
* Copyright (C) 2025 The Android Open Source Project | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#ifndef TNT_FILAMENT_BACKEND_WEBGPUTIMERQUERY_H | ||
#define TNT_FILAMENT_BACKEND_WEBGPUTIMERQUERY_H | ||
|
||
#include "DriverBase.h" | ||
|
||
#include <webgpu/webgpu_cpp.h> | ||
|
||
#include <atomic> | ||
#include <cstdint> | ||
#include <memory> | ||
|
||
namespace filament::backend { | ||
|
||
class WebGPUTimerQuery : public HwTimerQuery { | ||
public: | ||
WebGPUTimerQuery() | ||
: mStatus(std::make_shared<Status>()) {} | ||
|
||
void beginTimeElapsedQuery(); | ||
void endTimeElapsedQuery(); | ||
bool getQueryResult(uint64_t* outElapsedTimeNanoseconds); | ||
|
||
private: | ||
struct Status { | ||
std::atomic<uint64_t> elapsedNanoseconds{ 0 }; | ||
std::atomic<uint64_t> previousElapsed{ 0 }; | ||
}; | ||
|
||
std::shared_ptr<Status> mStatus; | ||
}; | ||
|
||
} // namespace filament::backend | ||
|
||
#endif //TNT_FILAMENT_BACKEND_WEBGPUTIMERQUERY_H |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually think moving this down here makes navigating the functions even more "difficult", because we are not consistent about it. I suggest rearranging them all at once in a PR to keep things consistent and easy to find.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could also just do it little by little
I dont think that it makes navigation more difficult, specially for Timer Queries
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's why I put double quotes around "difficult", because it is not particularly so, but I am just pointing out that I'm not a fan of the inconsistency.