Skip to content

webgpu: debug descriptor sets option/functionality #8712

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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/src/webgpu/WebGPUConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
// order of calls).
#define FWGPU_DEBUG_FORCE_LOG_TO_I 0x00000004

#define FWGPU_DEBUG_DESCRIPTOR_SETS 0x00000008

// Useful default combinations
#define FWGPU_DEBUG_EVERYTHING 0xFFFFFFFF

Expand Down
8 changes: 4 additions & 4 deletions filament/backend/src/webgpu/WebGPUDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,9 +470,8 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
assert_invariant(mSwapChain);
WebGPUDescriptorSet::initializeDummyResourcesIfNotAlready(mDevice,
mSwapChain->getColorFormat());
FWGPU_LOGW << "WebGPU support is still essentially a no-op at this point in development (only "
"background components have been instantiated/selected, such as surface/screen, "
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
FWGPU_LOGW << "WebGPU support is highly limited, only supporting the hello-triangle sample in "
"a brittle/hacked way and untested, likely, breaking in other samples."
<< utils::io::endl;
#if !FWGPU_ENABLED(FWGPU_PRINT_SYSTEM) && !defined(NDEBUG)
FWGPU_LOGI << "If the FILAMENT_BACKEND_DEBUG_FLAG variable were set with the " << utils::io::hex
Expand Down Expand Up @@ -591,7 +590,8 @@ void WebGPUDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
Handle<HwDescriptorSetLayout> dslh) {
auto layout = handleCast<WebGPUDescriptorSetLayout>(dslh);
constructHandle<WebGPUDescriptorSet>(dsh, layout->getLayout(), layout->getBindGroupEntries());
constructHandle<WebGPUDescriptorSet>(dsh, wgpu::StringView(layout->getLabel().c_str()),
layout->getLayout(), layout->getBindGroupEntries());
}

Handle<HwStream> WebGPUDriver::createStreamNative(void* nativeStream) {
Expand Down
224 changes: 219 additions & 5 deletions filament/backend/src/webgpu/WebGPUHandles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,27 @@

#include "WebGPUHandles.h"

#include "WebGPUConstants.h"

#include <backend/DriverEnums.h>

#include <utils/BitmaskEnum.h>
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
#include <utils/CString.h>
#include <utils/StaticString.h>
#endif

#include <webgpu/webgpu_cpp.h>
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
#include <webgpu/webgpu_cpp_print.h>
#endif

#include <algorithm>
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
#include <bitset>
#include <string_view>
#include <sstream>
#endif
#include <cstdint>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -157,6 +171,196 @@ wgpu::StringView getUserTextureViewLabel(filament::backend::SamplerType target)
}
}

#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
std::string_view toString(filament::backend::DescriptorType type) {
using filament::backend::DescriptorType;
switch (type) {
case DescriptorType::UNIFORM_BUFFER:
return "UNIFORM_BUFFER";
case DescriptorType::SHADER_STORAGE_BUFFER:
return "SHADER_STORAGE_BUFFER";
case DescriptorType::SAMPLER:
return "SAMPLER";
case DescriptorType::INPUT_ATTACHMENT:
return "INPUT_ATTACHMENT";
case DescriptorType::SAMPLER_EXTERNAL:
return "SAMPLER_EXTERNAL";
}
}
#endif

#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
std::string_view toString(filament::backend::ShaderStageFlags flags) {
using filament::backend::ShaderStageFlags;
switch (flags) {
case ShaderStageFlags::NONE:
return "NONE";
case ShaderStageFlags::VERTEX:
return "VERTEX";
case ShaderStageFlags::FRAGMENT:
return "FRAGMENT";
case ShaderStageFlags::COMPUTE:
return "COMPUTE";
case ShaderStageFlags::ALL_SHADER_STAGE_FLAGS:
return "ALL_SHADER_STAGE_FLAGS";
}
if (any(flags & ShaderStageFlags::VERTEX)) {
if (any(flags & ShaderStageFlags::FRAGMENT)) {
return "VERTEX|FRAGMENT";
}
if (any(flags & ShaderStageFlags::COMPUTE)) {
return "VERTEX|COMPUTE";
}
}
if (any(flags & ShaderStageFlags::FRAGMENT)) {
if (any(flags & ShaderStageFlags::COMPUTE)) {
return "FRAGMENT|COMPUTE";
}
}
}
#endif

#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
std::string_view toString(filament::backend::DescriptorFlags flags) {
using filament::backend::DescriptorFlags;
switch (flags) {
case DescriptorFlags::NONE:
return "NONE";
case DescriptorFlags::DYNAMIC_OFFSET:
return "DYNAMIC_OFFSET";
}
}
#endif

#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
void printFilamentDescriptorSetLayout(filament::backend::DescriptorSetLayout const& layout) {
const char* label;
if (std::holds_alternative<utils::StaticString>(layout.label)) {
label = std::get<utils::StaticString>(layout.label).c_str();
} else {
assert_invariant(std::holds_alternative<utils::CString>(layout.label));
label = std::get<utils::CString>(layout.label).c_str();
}
FWGPU_LOGD << "filament::backend::DescriptorSetLayout \"" << label << "\":" << utils::io::endl;
FWGPU_LOGD << " bindings (" << layout.bindings.size() << "):" << utils::io::endl;
for (filament::backend::DescriptorSetLayoutBinding const& binding: layout.bindings) {
FWGPU_LOGD << " binding " << static_cast<uint32_t>(binding.binding) << ": type "
<< toString(binding.type) << " stageFlags " << toString(binding.stageFlags)
<< " flags " << toString(binding.flags) << " count " << binding.count
<< utils::io::endl;
}
}
#endif

#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
void printBindGroupLayout(wgpu::BindGroupLayoutDescriptor const& layout) {
FWGPU_LOGD << "wgpu::BindGroupLayoutDescriptor label \"" << layout.label
<< "\":" << utils::io::endl;
FWGPU_LOGD << " entries (" << layout.entryCount << "):" << utils::io::endl;
for (size_t entryIndex = 0; entryIndex < layout.entryCount; entryIndex++) {
wgpu::BindGroupLayoutEntry const& entry = layout.entries[entryIndex];
std::stringstream visibilityStream;
visibilityStream << entry.visibility;
FWGPU_LOGD << " binding " << entry.binding << " " << visibilityStream.str();
assert_invariant(
(entry.buffer.type != wgpu::BufferBindingType::BindingNotUsed ||
entry.sampler.type != wgpu::SamplerBindingType::BindingNotUsed ||
entry.texture.sampleType != wgpu::TextureSampleType::BindingNotUsed ||
entry.storageTexture.access !=
wgpu::StorageTextureAccess::BindingNotUsed) &&
"None of buffer, sampler, texture, or storageTexture bound in the bind "
"group layout entry?");
if (entry.buffer.type != wgpu::BufferBindingType::BindingNotUsed &&
entry.buffer.type != wgpu::BufferBindingType::Undefined) {
assert_invariant(
entry.sampler.type == wgpu::SamplerBindingType::BindingNotUsed &&
entry.texture.sampleType == wgpu::TextureSampleType::BindingNotUsed &&
entry.storageTexture.access == wgpu::StorageTextureAccess::BindingNotUsed &&
"buffer binding used but also sampler and/or texture and/or storageTexture?");
std::stringstream typeStream;
typeStream << entry.buffer.type;
FWGPU_LOGD << " " << typeStream.str() << " hasDynamicOffset "
<< bool(entry.buffer.hasDynamicOffset) << " minBindingSize "
<< entry.buffer.minBindingSize;
}
if (entry.sampler.type != wgpu::SamplerBindingType::BindingNotUsed &&
entry.sampler.type != wgpu::SamplerBindingType::Undefined) {
assert_invariant(
entry.buffer.type == wgpu::BufferBindingType::BindingNotUsed &&
entry.texture.sampleType == wgpu::TextureSampleType::BindingNotUsed &&
entry.storageTexture.access == wgpu::StorageTextureAccess::BindingNotUsed &&
"sampler binding used but also buffer and/or texture and/or storageTexture?");
std::stringstream typeStream;
typeStream << entry.sampler.type;
FWGPU_LOGD << " " << typeStream.str();
}
if (entry.texture.sampleType != wgpu::TextureSampleType::BindingNotUsed &&
entry.texture.sampleType != wgpu::TextureSampleType::Undefined) {
assert_invariant(
entry.buffer.type == wgpu::BufferBindingType::BindingNotUsed &&
entry.sampler.type == wgpu::SamplerBindingType::BindingNotUsed &&
entry.storageTexture.access == wgpu::StorageTextureAccess::BindingNotUsed &&
"texture binding used but also buffer and/or sampler and/or storageTexture?");
std::stringstream typeStream;
typeStream << entry.texture.sampleType;
std::stringstream viewDimensionStream;
viewDimensionStream << entry.texture.viewDimension;
FWGPU_LOGD << " " << typeStream.str() << " " << viewDimensionStream.str()
<< " multisampled " << bool(entry.texture.multisampled);
}
if (entry.storageTexture.access != wgpu::StorageTextureAccess::BindingNotUsed &&
entry.storageTexture.access != wgpu::StorageTextureAccess::Undefined) {
assert_invariant(
entry.buffer.type == wgpu::BufferBindingType::BindingNotUsed &&
entry.sampler.type == wgpu::SamplerBindingType::BindingNotUsed &&
entry.texture.sampleType == wgpu::TextureSampleType::BindingNotUsed &&
"storageTexture binding used but also buffer and/or sampler and/or texture?");
std::stringstream accessStream;
accessStream << entry.storageTexture.access;
std::stringstream formatStream;
formatStream << entry.storageTexture.format;
std::stringstream viewDimensionStream;
viewDimensionStream << entry.storageTexture.viewDimension;
FWGPU_LOGD << " " << accessStream.str() << " " << formatStream.str() << " "
<< viewDimensionStream.str();
}
FWGPU_LOGD << utils::io::endl;
}
}
#endif

#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
template<size_t DESCRIPTOR_COUNT>
void printBindGroup(wgpu::BindGroupDescriptor const& bindGroup,
std::bitset<DESCRIPTOR_COUNT> entriesByBindingWithDynamicOffsets,
std::bitset<DESCRIPTOR_COUNT> entriesByBindingAdded) {
FWGPU_LOGD << "wgpu::BindGroupDescriptor label \"" << bindGroup.label << "\":" << utils::io::endl;
FWGPU_LOGD << " entries (" << bindGroup.entryCount << "):" << utils::io::endl;
for (size_t entryIndex = 0; entryIndex < bindGroup.entryCount; entryIndex++) {
wgpu::BindGroupEntry const& entry = bindGroup.entries[entryIndex];
FWGPU_LOGD << " binding " << entry.binding;
assert_invariant((entry.buffer || entry.sampler || entry.textureView) &&
"none of buffer, sampler, or textureView provided in bind group entry?");
if (entry.buffer) {
assert_invariant(entry.sampler == nullptr && entry.textureView == nullptr &&
"bind group entry with buffer also has sampler and/or textureView?");
FWGPU_LOGD << " buffer";
}
if (entry.sampler) {
assert_invariant(entry.buffer == nullptr && entry.textureView == nullptr &&
"bind group entry with sampler also has buffer and/or textureView?");
FWGPU_LOGD << " sampler";
}
if (entry.textureView) {
assert_invariant(entry.buffer == nullptr && entry.sampler == nullptr &&
"bind group entry with textureView also has buffer and/or sampler?");
FWGPU_LOGD << " textureView";
}
FWGPU_LOGD << " offset " << entry.offset << " size " << entry.size << utils::io::endl;
}
}
#endif

}// namespace

namespace filament::backend {
Expand Down Expand Up @@ -237,6 +441,9 @@ wgpu::ShaderStage WebGPUDescriptorSetLayout::filamentStageToWGPUStage(ShaderStag

WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout,
wgpu::Device const& device) {
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
printFilamentDescriptorSetLayout(layout);
#endif
assert_invariant(device);

std::string baseLabel;
Expand Down Expand Up @@ -319,6 +526,10 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
.entryCount = wEntries.size(),
.entries = wEntries.data()
};
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
printBindGroupLayout(layoutDescriptor);
#endif
mLabel = utils::CString(label.c_str());
mLayout = device.CreateBindGroupLayout(&layoutDescriptor);
}

Expand Down Expand Up @@ -411,9 +622,11 @@ std::vector<wgpu::BindGroupEntry> WebGPUDescriptorSet::createDummyEntriesSortedB
return entries;
}

WebGPUDescriptorSet::WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,
WebGPUDescriptorSet::WebGPUDescriptorSet(wgpu::StringView label,
wgpu::BindGroupLayout const& layout,
std::vector<WebGPUDescriptorSetLayout::BindGroupEntryInfo> const& bindGroupEntries)
: mLayout(layout),
: mLabel(label),
mLayout(layout),
mEntriesSortedByBinding(createDummyEntriesSortedByBinding(bindGroupEntries)) {
// Establish the size of entries based on the layout. This should be reliable and efficient.
assert_invariant(INVALID_INDEX > mEntryIndexByBinding.size());
Expand All @@ -436,23 +649,24 @@ WebGPUDescriptorSet::WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,

WebGPUDescriptorSet::~WebGPUDescriptorSet() {
mBindGroup = nullptr;
mLayout = nullptr;
}

wgpu::BindGroup WebGPUDescriptorSet::lockAndReturn(const wgpu::Device& device) {
if (mBindGroup) {
return mBindGroup;
}
// TODO label? Should we just copy layout label?
wgpu::BindGroupDescriptor desc{
.label = mLabel,
.layout = mLayout,
.entryCount = mEntriesSortedByBinding.size(),
.entries = mEntriesSortedByBinding.data()
};
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
printBindGroup(desc, mEntriesByBindingWithDynamicOffsets, mEntriesByBindingAdded);
#endif
mBindGroup = device.CreateBindGroup(&desc);
FILAMENT_CHECK_POSTCONDITION(mBindGroup) << "Failed to create bind group?";
// once we have created the bind group itself we should no longer need any other state
mLayout = nullptr;
mEntriesSortedByBinding.clear();
mEntriesSortedByBinding.shrink_to_fit();
return mBindGroup;
Expand Down
11 changes: 8 additions & 3 deletions filament/backend/src/webgpu/WebGPUHandles.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* limitations under the License.
*/


#ifndef TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
#define TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H

Expand All @@ -24,6 +23,7 @@
#include <backend/Handle.h>

#include <utils/FixedCapacityVector.h>
#include <utils/CString.h>

#include <webgpu/webgpu_cpp.h>

Expand Down Expand Up @@ -118,6 +118,8 @@ class WebGPUDescriptorSetLayout final : public HwDescriptorSetLayout {

WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout, wgpu::Device const& device);
~WebGPUDescriptorSetLayout();

[[nodiscard]] utils::CString const& getLabel() const { return mLabel; }
[[nodiscard]] const wgpu::BindGroupLayout& getLayout() const { return mLayout; }
[[nodiscard]] std::vector<BindGroupEntryInfo> const& getBindGroupEntries() const {
return mBindGroupEntries;
Expand All @@ -127,6 +129,8 @@ class WebGPUDescriptorSetLayout final : public HwDescriptorSetLayout {
// TODO: If this is useful elsewhere, remove it from this class
// Convert Filament Shader Stage Flags bitmask to webgpu equivilant
static wgpu::ShaderStage filamentStageToWGPUStage(ShaderStageFlags fFlags);

utils::CString mLabel;
std::vector<BindGroupEntryInfo> mBindGroupEntries;
wgpu::BindGroupLayout mLayout;
};
Expand All @@ -136,7 +140,7 @@ class WebGPUDescriptorSet final : public HwDescriptorSet {
static void initializeDummyResourcesIfNotAlready(wgpu::Device const&,
wgpu::TextureFormat aColorFormat);

WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,
WebGPUDescriptorSet(wgpu::StringView label, wgpu::BindGroupLayout const& layout,
std::vector<WebGPUDescriptorSetLayout::BindGroupEntryInfo> const& bindGroupEntries);
~WebGPUDescriptorSet();

Expand All @@ -155,11 +159,12 @@ class WebGPUDescriptorSet final : public HwDescriptorSet {
static std::vector<wgpu::BindGroupEntry> createDummyEntriesSortedByBinding(
std::vector<filament::backend::WebGPUDescriptorSetLayout::BindGroupEntryInfo> const&);

const wgpu::StringView mLabel;
// TODO: Consider storing what we used to make the layout. However we need to essentially
// Recreate some of the info (Sampler in slot X with the actual sampler) so letting Dawn confirm
// there isn't a mismatch may be easiest.
// Also storing the wgpu ObjectBase takes care of ownership challenges in theory
wgpu::BindGroupLayout mLayout = nullptr;
wgpu::BindGroupLayout const& mLayout;
static constexpr uint8_t INVALID_INDEX = MAX_DESCRIPTOR_COUNT + 1;
std::array<uint8_t, MAX_DESCRIPTOR_COUNT> mEntryIndexByBinding {};
std::vector<wgpu::BindGroupEntry> mEntriesSortedByBinding;
Expand Down
Loading