Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions filament/backend/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,8 @@ if (FILAMENT_SUPPORTS_WEBGPU)
src/webgpu/WebGPUSwapChain.h
src/webgpu/WebGPUTexture.cpp
src/webgpu/WebGPUTexture.h
src/webgpu/WebGPUTimerQuery.cpp
src/webgpu/WebGPUTimerQuery.h
src/webgpu/WebGPUVertexBuffer.cpp
src/webgpu/WebGPUVertexBuffer.h
src/webgpu/WebGPUVertexBufferInfo.cpp
Expand Down
58 changes: 40 additions & 18 deletions filament/backend/src/webgpu/WebGPUDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Copy link
Contributor

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.

Copy link
Contributor Author

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

Copy link
Contributor

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.

if (timerQueryHandle) {
destructHandle<WebGPUTimerQuery>(timerQueryHandle);
}
}

TimerQueryResult WebGPUDriver::getTimerQueryValue(Handle<HwTimerQuery> timerQueryHandle, uint64_t* elapsedTime) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: rename elapsedTime to outElapsedTime to indicate it as an output parameter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

elapsedTime matches DriverApi.inc

DECL_DRIVER_API_SYNCHRONOUS_N(backend::TimerQueryResult, getTimerQueryValue, backend::TimerQueryHandle, query, uint64_t*, elapsedTime)```

Copy link
Contributor

Choose a reason for hiding this comment

The 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 timerQueryHandle instead of query from DriverApi.inc.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I would make the pointer const (but, not what it points to, as it is an output param), e.g. uint64_t* const outElapsedTime.

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 {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
Expand All @@ -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();
Copy link
Contributor

Choose a reason for hiding this comment

The 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;
Expand Down Expand Up @@ -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
}
Expand Down
7 changes: 7 additions & 0 deletions filament/backend/src/webgpu/WebGPUDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
namespace filament::backend {

class WebGPUSwapChain;
class WebGPUTimerQuery;

/**
* WebGPU backend (driver) implementation
Expand Down Expand Up @@ -78,6 +79,7 @@ class WebGPUDriver final : public DriverBase {
WebGPURenderTarget* mDefaultRenderTarget = nullptr;
WebGPURenderTarget* mCurrentRenderTarget = nullptr;
spd::MipmapGenerator mMipMapGenerator;
WebGPUTimerQuery* mTimerQuery = nullptr;

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

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

template<typename D, typename B, typename... ARGS>
Handle<B> allocAndConstructHandle(ARGS&&... args) {
return mHandleAllocator.allocateAndConstruct<D>(std::forward<ARGS>(args)...);
}

template<typename D, typename B, typename... ARGS>
D* constructHandle(Handle<B>& handle, ARGS&&... args) noexcept {
return mHandleAllocator.construct<D>(handle, std::forward<ARGS>(args)...);
Expand Down
60 changes: 60 additions & 0 deletions filament/backend/src/webgpu/WebGPUTimerQuery.cpp
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
50 changes: 50 additions & 0 deletions filament/backend/src/webgpu/WebGPUTimerQuery.h
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
Loading