Skip to content

webgpu: Implement Tqueries #8669

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

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions filament/backend/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ if (FILAMENT_SUPPORTS_WEBGPU)
src/webgpu/WebGPUSwapChain.cpp
src/webgpu/WebGPUSwapChain.h
src/webgpu/WGPUProgram.cpp
src/webgpu/WGPUTimerQuery.cpp
)
if (WIN32)
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
Expand Down
69 changes: 69 additions & 0 deletions filament/backend/src/webgpu/WGPUTimerQuery.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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 "WebGPUHandles.h"

#include <chrono>
#include <memory>
#include <iostream>

Copy link
Contributor

Choose a reason for hiding this comment

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

you need to include (include-what-you-use) <memory> for std::weak_ptr and <cstdint> for uint64_t.

namespace filament::backend {

void WGPUTimerQuery::beginTimeElapsedQuery() {
std::cout << " WGPUTimerQuery::beginTimeElapsedQuery()" <<std::endl;
status->elapsedNanoseconds = 0;
// Capture the timer query status via a weak_ptr because the WGPUTimerQuery could be destroyed
// before the block executes.
std::weak_ptr<WGPUTimerQuery::Status> statusPtr = status;

if (auto s = statusPtr.lock()) {
s->elapsedNanoseconds = std::chrono::steady_clock::now().time_since_epoch().count();
}
}

void WGPUTimerQuery::endTimeElapsedQuery() {
std::cout << " WGPUTimerQuery::endTimeElapsedQuery()" <<std::endl;
// Capture the timer query status via a weak_ptr because the WGPUTimerQuery could be destroyed
// before the block executes.
if (status->elapsedNanoseconds == 0)
{
std::cout << " %%%%%%%%WGPUTimerQuery::endTimeElapsedQuery()" <<std::endl;
}
else {
std::weak_ptr<WGPUTimerQuery::Status> statusPtr = status;
if (auto s = statusPtr.lock()) {
s->previousElapsed = s->elapsedNanoseconds =
std::chrono::steady_clock::now().time_since_epoch().count() -
s->elapsedNanoseconds;
}
}
}

bool WGPUTimerQuery::getQueryResult(uint64_t* outElapsedTime) {
std::cout << " WGPUTimerQuery::getQueryResult: " << std::endl;
if (status->previousElapsed == 0) {
return false;
}
if (outElapsedTime) {
Copy link
Contributor

Choose a reason for hiding this comment

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

First, this should not ever be true right? If the status is available, the time should be. Perhaps an assert_invariant(...) makes more sense? Second, even if this were true we would return that the results are available, but we never set anything in outElapsedTime which could have undefined behavior, such as junk in that integer.

*outElapsedTime = status->previousElapsed;
std::cout << " TimeResult: " << status->previousElapsed << std::endl;
status->previousElapsed = 0;
}
return true;
}

}// namespace filament::backend
80 changes: 57 additions & 23 deletions filament/backend/src/webgpu/WebGPUDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
* limitations under the License.
*/
#include <utils/Hash.h>
#include "webgpu/WebGPUConstants.h"
#include "webgpu/WebGPUDriver.h"
#include "WebGPUHandles.h"

#include "WebGPUPipelineCreation.h"
#include "WebGPUSwapChain.h"
Expand Down Expand Up @@ -43,7 +45,7 @@
#include <string_view>
#include <utility>
#include <variant>

#include <iostream>
namespace filament::backend {

namespace {
Expand Down Expand Up @@ -311,13 +313,13 @@ void WebGPUDriver::finish(int /* dummy */) {
}

void WebGPUDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
if (rph) {
if (rph) {
destructHandle<WGPURenderPrimitive>(rph);
}
}

void WebGPUDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
if (vbih) {
if (vbih) {
destructHandle<WGPUVertexBufferInfo>(vbih);
}
}
Expand Down Expand Up @@ -359,11 +361,7 @@ void WebGPUDriver::destroySwapChain(Handle<HwSwapChain> sch) {
mSwapChain = nullptr;
}

void WebGPUDriver::destroyStream(Handle<HwStream> sh) {
}

void WebGPUDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
}
void WebGPUDriver::destroyStream(Handle<HwStream> sh) {}

void WebGPUDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh) {
if (tqh) {
Expand Down Expand Up @@ -400,7 +398,36 @@ Handle<HwFence> WebGPUDriver::createFenceS() noexcept {
}

Handle<HwTimerQuery> WebGPUDriver::createTimerQueryS() noexcept {
return Handle<HwTimerQuery>((Handle<HwTimerQuery>::HandleId) mNextFakeHandle++);
// The handle must be constructed here, as a synchronous call to getTimerQueryValue might happen
// before createTimerQueryR is executed.
return allocAndConstructHandle<WGPUTimerQuery, HwTimerQuery>();
}

void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int /* dummy */) {
// nothing to do, timer query was constructed in createTimerQueryS
}

void WebGPUDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
if (tqh) {
destructHandle<WGPUTimerQuery>(tqh);
}
}

void WebGPUDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
std::cout << "WebGPUDriver::beginTimerQuery" <<std::endl;
mTimerQuery = handleCast<WGPUTimerQuery>(tqh);
}

void WebGPUDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
std::cout << "WebGPUDriver::endTimerQuery" <<std::endl;
mTimerQuery = handleCast<WGPUTimerQuery>(tqh);
}

TimerQueryResult WebGPUDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
std::cout << "WebGPUDriver::getTimerQueryValue" <<std::endl;
auto* tq = handleCast<WGPUTimerQuery>(tqh);
return tq->getQueryResult(elapsedTime) ? TimerQueryResult::AVAILABLE
: TimerQueryResult::NOT_READY;
}

Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
Expand Down Expand Up @@ -581,7 +608,6 @@ void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferF

void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {}

void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}

void WebGPUDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
backend::DescriptorSetLayout&& info) {
Expand Down Expand Up @@ -757,17 +783,14 @@ void WebGPUDriver::update3DImage(Handle<HwTexture> th,
void WebGPUDriver::setupExternalImage(void* image) {
}

TimerQueryResult WebGPUDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
return TimerQueryResult::ERROR;
}

void WebGPUDriver::setupExternalImage2(Platform::ExternalImageHandleRef image) {
}

void WebGPUDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh) {
}

void WebGPUDriver::generateMipmaps(Handle<HwTexture> th) { }
void WebGPUDriver::generateMipmaps(Handle<HwTexture> th) {
}

void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
Expand Down Expand Up @@ -809,6 +832,7 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, RenderPassParams
red = 1.0f;
}
assert_invariant(mTextureView);

wgpu::RenderPassColorAttachment renderPassColorAttachment = {
.view = mTextureView,
// TODO: remove this code once WebGPU Pipeline is implemented with render targets, pipeline and buffers.
Expand Down Expand Up @@ -855,12 +879,29 @@ void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
}

void WebGPUDriver::commit(Handle<HwSwapChain> sch) {
// mTimerQuery->beginTimeElapsedQuery();
wgpu::CommandBufferDescriptor commandBufferDescriptor{
.label = "command_buffer",
};
mCommandBuffer = mCommandEncoder.Finish(&commandBufferDescriptor);
assert_invariant(mCommandBuffer);
mCommandEncoder = nullptr;
assert_invariant(mCommandBuffer);
mQueue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowSpontaneous,
[this](auto const& status) {
std::cout << "OnSubmittedWorkDone called " << std::endl;
if (status == wgpu::QueueWorkDoneStatus::Success) {
if (mTimerQuery) {
mTimerQuery->endTimeElapsedQuery();
}
}
else
{
std::cout << "NOT SUCCESS" << std::endl;
}
});

mTimerQuery->beginTimeElapsedQuery();
mQueue.Submit(1, &mCommandBuffer);
mCommandBuffer = nullptr;
mTextureView = nullptr;
Expand Down Expand Up @@ -1002,14 +1043,8 @@ void WebGPUDriver::scissor(
Viewport scissor) {
}

void WebGPUDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
}

void WebGPUDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
}

void WebGPUDriver::resetState(int) {
}
void WebGPUDriver::resetState(int) {}

void WebGPUDriver::updateDescriptorSetBuffer(Handle<HwDescriptorSet> dsh,
backend::descriptor_binding_t binding, Handle<HwBufferObject> boh, uint32_t offset,
Expand Down Expand Up @@ -1179,5 +1214,4 @@ wgpu::AddressMode WebGPUDriver::fWrapModeToWAddressMode(const SamplerWrapMode& f
return wgpu::AddressMode::Undefined;
}


} // namespace filament
10 changes: 8 additions & 2 deletions filament/backend/src/webgpu/WebGPUDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
#ifndef TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
#define TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H

#include "WebGPUHandles.h"
#include "webgpu/WebGPUConstants.h"
#include <backend/platforms/WebGPUPlatform.h>

#include "DriverBase.h"
Expand All @@ -41,6 +39,8 @@
namespace filament::backend {

class WebGPUSwapChain;
class WGPURenderTarget;
class WGPUTimerQuery;

/**
* WebGPU backend (driver) implementation
Expand Down Expand Up @@ -93,6 +93,7 @@ class WebGPUDriver final : public DriverBase {
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
wgpu::CommandBuffer mCommandBuffer = nullptr;
WGPURenderTarget* mDefaultRenderTarget = nullptr;
WGPUTimerQuery* mTimerQuery = nullptr;

tsl::robin_map<uint32_t, wgpu::RenderPipeline> mPipelineMap;
/*
Expand Down Expand Up @@ -129,6 +130,11 @@ class WebGPUDriver final : public DriverBase {
return mHandleAllocator.construct<D>(handle, std::forward<ARGS>(args)...);
}

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>
D* handleCast(Handle<B> handle) noexcept {
return mHandleAllocator.handle_cast<D*>(handle);
Expand Down
18 changes: 18 additions & 0 deletions filament/backend/src/webgpu/WebGPUHandles.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,5 +259,23 @@ class WGPURenderTarget : public HwRenderTarget {
std::vector<wgpu::RenderPassColorAttachment> colorAttachments{};
};

class WGPUTimerQuery : public HwTimerQuery {
public:
WGPUTimerQuery()
: status(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> status;
};

}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
Loading