Skip to content

Commit 61306c7

Browse files
committed
webgpu: debug descriptor sets option/functionality
1 parent 44a75dd commit 61306c7

File tree

4 files changed

+233
-12
lines changed

4 files changed

+233
-12
lines changed

filament/backend/src/webgpu/WebGPUConstants.h

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
// order of calls).
3535
#define FWGPU_DEBUG_FORCE_LOG_TO_I 0x00000004
3636

37+
#define FWGPU_DEBUG_DESCRIPTOR_SETS 0x00000008
38+
3739
// Useful default combinations
3840
#define FWGPU_DEBUG_EVERYTHING 0xFFFFFFFF
3941

filament/backend/src/webgpu/WebGPUDriver.cpp

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

597597
Handle<HwStream> WebGPUDriver::createStreamNative(void* nativeStream) {

filament/backend/src/webgpu/WebGPUHandles.cpp

+219-5
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,27 @@
1616

1717
#include "WebGPUHandles.h"
1818

19+
#include "WebGPUConstants.h"
20+
1921
#include <backend/DriverEnums.h>
2022

2123
#include <utils/BitmaskEnum.h>
24+
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
25+
#include <utils/CString.h>
26+
#include <utils/StaticString.h>
27+
#endif
2228

2329
#include <webgpu/webgpu_cpp.h>
30+
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
31+
#include <webgpu/webgpu_cpp_print.h>
32+
#endif
2433

2534
#include <algorithm>
35+
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
36+
#include <bitset>
37+
#include <string_view>
38+
#include <sstream>
39+
#endif
2640
#include <cstdint>
2741
#include <utility>
2842
#include <vector>
@@ -157,6 +171,196 @@ wgpu::StringView getUserTextureViewLabel(filament::backend::SamplerType target)
157171
}
158172
}
159173

174+
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
175+
std::string_view toString(filament::backend::DescriptorType type) {
176+
using filament::backend::DescriptorType;
177+
switch (type) {
178+
case DescriptorType::UNIFORM_BUFFER:
179+
return "UNIFORM_BUFFER";
180+
case DescriptorType::SHADER_STORAGE_BUFFER:
181+
return "SHADER_STORAGE_BUFFER";
182+
case DescriptorType::SAMPLER:
183+
return "SAMPLER";
184+
case DescriptorType::INPUT_ATTACHMENT:
185+
return "INPUT_ATTACHMENT";
186+
case DescriptorType::SAMPLER_EXTERNAL:
187+
return "SAMPLER_EXTERNAL";
188+
}
189+
}
190+
#endif
191+
192+
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
193+
std::string_view toString(filament::backend::ShaderStageFlags flags) {
194+
using filament::backend::ShaderStageFlags;
195+
switch (flags) {
196+
case ShaderStageFlags::NONE:
197+
return "NONE";
198+
case ShaderStageFlags::VERTEX:
199+
return "VERTEX";
200+
case ShaderStageFlags::FRAGMENT:
201+
return "FRAGMENT";
202+
case ShaderStageFlags::COMPUTE:
203+
return "COMPUTE";
204+
case ShaderStageFlags::ALL_SHADER_STAGE_FLAGS:
205+
return "ALL_SHADER_STAGE_FLAGS";
206+
}
207+
if (any(flags & ShaderStageFlags::VERTEX)) {
208+
if (any(flags & ShaderStageFlags::FRAGMENT)) {
209+
return "VERTEX|FRAGMENT";
210+
}
211+
if (any(flags & ShaderStageFlags::COMPUTE)) {
212+
return "VERTEX|COMPUTE";
213+
}
214+
}
215+
if (any(flags & ShaderStageFlags::FRAGMENT)) {
216+
if (any(flags & ShaderStageFlags::COMPUTE)) {
217+
return "FRAGMENT|COMPUTE";
218+
}
219+
}
220+
}
221+
#endif
222+
223+
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
224+
std::string_view toString(filament::backend::DescriptorFlags flags) {
225+
using filament::backend::DescriptorFlags;
226+
switch (flags) {
227+
case DescriptorFlags::NONE:
228+
return "NONE";
229+
case DescriptorFlags::DYNAMIC_OFFSET:
230+
return "DYNAMIC_OFFSET";
231+
}
232+
}
233+
#endif
234+
235+
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
236+
void printFilamentDescriptorSetLayout(filament::backend::DescriptorSetLayout const& layout) {
237+
const char* label;
238+
if (std::holds_alternative<utils::StaticString>(layout.label)) {
239+
label = std::get<utils::StaticString>(layout.label).c_str();
240+
} else {
241+
assert_invariant(std::holds_alternative<utils::CString>(layout.label));
242+
label = std::get<utils::CString>(layout.label).c_str();
243+
}
244+
FWGPU_LOGD << "filament::backend::DescriptorSetLayout \"" << label << "\":" << utils::io::endl;
245+
FWGPU_LOGD << " bindings (" << layout.bindings.size() << "):" << utils::io::endl;
246+
for (filament::backend::DescriptorSetLayoutBinding const& binding: layout.bindings) {
247+
FWGPU_LOGD << " binding " << static_cast<uint32_t>(binding.binding) << ": type "
248+
<< toString(binding.type) << " stageFlags " << toString(binding.stageFlags)
249+
<< " flags " << toString(binding.flags) << " count " << binding.count
250+
<< utils::io::endl;
251+
}
252+
}
253+
#endif
254+
255+
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
256+
void printBindGroupLayout(wgpu::BindGroupLayoutDescriptor const& layout) {
257+
FWGPU_LOGD << "wgpu::BindGroupLayoutDescriptor label \"" << layout.label
258+
<< "\":" << utils::io::endl;
259+
FWGPU_LOGD << " entries (" << layout.entryCount << "):" << utils::io::endl;
260+
for (size_t entryIndex = 0; entryIndex < layout.entryCount; entryIndex++) {
261+
wgpu::BindGroupLayoutEntry const& entry = layout.entries[entryIndex];
262+
std::stringstream visibilityStream;
263+
visibilityStream << entry.visibility;
264+
FWGPU_LOGD << " binding " << entry.binding << " " << visibilityStream.str();
265+
assert_invariant(
266+
(entry.buffer.type != wgpu::BufferBindingType::BindingNotUsed ||
267+
entry.sampler.type != wgpu::SamplerBindingType::BindingNotUsed ||
268+
entry.texture.sampleType != wgpu::TextureSampleType::BindingNotUsed ||
269+
entry.storageTexture.access !=
270+
wgpu::StorageTextureAccess::BindingNotUsed) &&
271+
"None of buffer, sampler, texture, or storageTexture bound in the bind "
272+
"group layout entry?");
273+
if (entry.buffer.type != wgpu::BufferBindingType::BindingNotUsed &&
274+
entry.buffer.type != wgpu::BufferBindingType::Undefined) {
275+
assert_invariant(
276+
entry.sampler.type == wgpu::SamplerBindingType::BindingNotUsed &&
277+
entry.texture.sampleType == wgpu::TextureSampleType::BindingNotUsed &&
278+
entry.storageTexture.access == wgpu::StorageTextureAccess::BindingNotUsed &&
279+
"buffer binding used but also sampler and/or texture and/or storageTexture?");
280+
std::stringstream typeStream;
281+
typeStream << entry.buffer.type;
282+
FWGPU_LOGD << " " << typeStream.str() << " hasDynamicOffset "
283+
<< bool(entry.buffer.hasDynamicOffset) << " minBindingSize "
284+
<< entry.buffer.minBindingSize;
285+
}
286+
if (entry.sampler.type != wgpu::SamplerBindingType::BindingNotUsed &&
287+
entry.sampler.type != wgpu::SamplerBindingType::Undefined) {
288+
assert_invariant(
289+
entry.buffer.type == wgpu::BufferBindingType::BindingNotUsed &&
290+
entry.texture.sampleType == wgpu::TextureSampleType::BindingNotUsed &&
291+
entry.storageTexture.access == wgpu::StorageTextureAccess::BindingNotUsed &&
292+
"sampler binding used but also buffer and/or texture and/or storageTexture?");
293+
std::stringstream typeStream;
294+
typeStream << entry.sampler.type;
295+
FWGPU_LOGD << " " << typeStream.str();
296+
}
297+
if (entry.texture.sampleType != wgpu::TextureSampleType::BindingNotUsed &&
298+
entry.texture.sampleType != wgpu::TextureSampleType::Undefined) {
299+
assert_invariant(
300+
entry.buffer.type == wgpu::BufferBindingType::BindingNotUsed &&
301+
entry.sampler.type == wgpu::SamplerBindingType::BindingNotUsed &&
302+
entry.storageTexture.access == wgpu::StorageTextureAccess::BindingNotUsed &&
303+
"texture binding used but also buffer and/or sampler and/or storageTexture?");
304+
std::stringstream typeStream;
305+
typeStream << entry.texture.sampleType;
306+
std::stringstream viewDimensionStream;
307+
viewDimensionStream << entry.texture.viewDimension;
308+
FWGPU_LOGD << " " << typeStream.str() << " " << viewDimensionStream.str()
309+
<< " multisampled " << bool(entry.texture.multisampled);
310+
}
311+
if (entry.storageTexture.access != wgpu::StorageTextureAccess::BindingNotUsed &&
312+
entry.storageTexture.access != wgpu::StorageTextureAccess::Undefined) {
313+
assert_invariant(
314+
entry.buffer.type == wgpu::BufferBindingType::BindingNotUsed &&
315+
entry.sampler.type == wgpu::SamplerBindingType::BindingNotUsed &&
316+
entry.texture.sampleType == wgpu::TextureSampleType::BindingNotUsed &&
317+
"storageTexture binding used but also buffer and/or sampler and/or texture?");
318+
std::stringstream accessStream;
319+
accessStream << entry.storageTexture.access;
320+
std::stringstream formatStream;
321+
formatStream << entry.storageTexture.format;
322+
std::stringstream viewDimensionStream;
323+
viewDimensionStream << entry.storageTexture.viewDimension;
324+
FWGPU_LOGD << " " << accessStream.str() << " " << formatStream.str() << " "
325+
<< viewDimensionStream.str();
326+
}
327+
FWGPU_LOGD << utils::io::endl;
328+
}
329+
}
330+
#endif
331+
332+
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
333+
template<size_t DESCRIPTOR_COUNT>
334+
void printBindGroup(wgpu::BindGroupDescriptor const& bindGroup,
335+
std::bitset<DESCRIPTOR_COUNT> entriesByBindingWithDynamicOffsets,
336+
std::bitset<DESCRIPTOR_COUNT> entriesByBindingAdded) {
337+
FWGPU_LOGD << "wgpu::BindGroupDescriptor label \"" << bindGroup.label << "\":" << utils::io::endl;
338+
FWGPU_LOGD << " entries (" << bindGroup.entryCount << "):" << utils::io::endl;
339+
for (size_t entryIndex = 0; entryIndex < bindGroup.entryCount; entryIndex++) {
340+
wgpu::BindGroupEntry const& entry = bindGroup.entries[entryIndex];
341+
FWGPU_LOGD << " binding " << entry.binding;
342+
assert_invariant((entry.buffer || entry.sampler || entry.textureView) &&
343+
"none of buffer, sampler, or textureView provided in bind group entry?");
344+
if (entry.buffer) {
345+
assert_invariant(entry.sampler == nullptr && entry.textureView == nullptr &&
346+
"bind group entry with buffer also has sampler and/or textureView?");
347+
FWGPU_LOGD << " buffer";
348+
}
349+
if (entry.sampler) {
350+
assert_invariant(entry.buffer == nullptr && entry.textureView == nullptr &&
351+
"bind group entry with sampler also has buffer and/or textureView?");
352+
FWGPU_LOGD << " sampler";
353+
}
354+
if (entry.textureView) {
355+
assert_invariant(entry.buffer == nullptr && entry.sampler == nullptr &&
356+
"bind group entry with textureView also has buffer and/or sampler?");
357+
FWGPU_LOGD << " textureView";
358+
}
359+
FWGPU_LOGD << " offset " << entry.offset << " size " << entry.size << utils::io::endl;
360+
}
361+
}
362+
#endif
363+
160364
}// namespace
161365

162366
namespace filament::backend {
@@ -237,6 +441,9 @@ wgpu::ShaderStage WebGPUDescriptorSetLayout::filamentStageToWGPUStage(ShaderStag
237441

238442
WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout,
239443
wgpu::Device const& device) {
444+
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
445+
printFilamentDescriptorSetLayout(layout);
446+
#endif
240447
assert_invariant(device);
241448

242449
std::string baseLabel;
@@ -319,6 +526,10 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
319526
.entryCount = wEntries.size(),
320527
.entries = wEntries.data()
321528
};
529+
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
530+
printBindGroupLayout(layoutDescriptor);
531+
#endif
532+
mLabel = utils::CString(label.c_str());
322533
mLayout = device.CreateBindGroupLayout(&layoutDescriptor);
323534
}
324535

@@ -411,9 +622,11 @@ std::vector<wgpu::BindGroupEntry> WebGPUDescriptorSet::createDummyEntriesSortedB
411622
return entries;
412623
}
413624

414-
WebGPUDescriptorSet::WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,
625+
WebGPUDescriptorSet::WebGPUDescriptorSet(wgpu::StringView label,
626+
wgpu::BindGroupLayout const& layout,
415627
std::vector<WebGPUDescriptorSetLayout::BindGroupEntryInfo> const& bindGroupEntries)
416-
: mLayout(layout),
628+
: mLabel(label),
629+
mLayout(layout),
417630
mEntriesSortedByBinding(createDummyEntriesSortedByBinding(bindGroupEntries)) {
418631
// Establish the size of entries based on the layout. This should be reliable and efficient.
419632
assert_invariant(INVALID_INDEX > mEntryIndexByBinding.size());
@@ -436,23 +649,24 @@ WebGPUDescriptorSet::WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,
436649

437650
WebGPUDescriptorSet::~WebGPUDescriptorSet() {
438651
mBindGroup = nullptr;
439-
mLayout = nullptr;
440652
}
441653

442654
wgpu::BindGroup WebGPUDescriptorSet::lockAndReturn(const wgpu::Device& device) {
443655
if (mBindGroup) {
444656
return mBindGroup;
445657
}
446-
// TODO label? Should we just copy layout label?
447658
wgpu::BindGroupDescriptor desc{
659+
.label = mLabel,
448660
.layout = mLayout,
449661
.entryCount = mEntriesSortedByBinding.size(),
450662
.entries = mEntriesSortedByBinding.data()
451663
};
664+
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
665+
printBindGroup(desc, mEntriesByBindingWithDynamicOffsets, mEntriesByBindingAdded);
666+
#endif
452667
mBindGroup = device.CreateBindGroup(&desc);
453668
FILAMENT_CHECK_POSTCONDITION(mBindGroup) << "Failed to create bind group?";
454669
// once we have created the bind group itself we should no longer need any other state
455-
mLayout = nullptr;
456670
mEntriesSortedByBinding.clear();
457671
mEntriesSortedByBinding.shrink_to_fit();
458672
return mBindGroup;

filament/backend/src/webgpu/WebGPUHandles.h

+8-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
* limitations under the License.
1515
*/
1616

17-
1817
#ifndef TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
1918
#define TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
2019

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

2625
#include <utils/FixedCapacityVector.h>
26+
#include <utils/CString.h>
2727

2828
#include <webgpu/webgpu_cpp.h>
2929

@@ -118,6 +118,8 @@ class WebGPUDescriptorSetLayout final : public HwDescriptorSetLayout {
118118

119119
WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout, wgpu::Device const& device);
120120
~WebGPUDescriptorSetLayout();
121+
122+
[[nodiscard]] utils::CString const& getLabel() const { return mLabel; }
121123
[[nodiscard]] const wgpu::BindGroupLayout& getLayout() const { return mLayout; }
122124
[[nodiscard]] std::vector<BindGroupEntryInfo> const& getBindGroupEntries() const {
123125
return mBindGroupEntries;
@@ -127,6 +129,8 @@ class WebGPUDescriptorSetLayout final : public HwDescriptorSetLayout {
127129
// TODO: If this is useful elsewhere, remove it from this class
128130
// Convert Filament Shader Stage Flags bitmask to webgpu equivilant
129131
static wgpu::ShaderStage filamentStageToWGPUStage(ShaderStageFlags fFlags);
132+
133+
utils::CString mLabel;
130134
std::vector<BindGroupEntryInfo> mBindGroupEntries;
131135
wgpu::BindGroupLayout mLayout;
132136
};
@@ -136,7 +140,7 @@ class WebGPUDescriptorSet final : public HwDescriptorSet {
136140
static void initializeDummyResourcesIfNotAlready(wgpu::Device const&,
137141
wgpu::TextureFormat aColorFormat);
138142

139-
WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,
143+
WebGPUDescriptorSet(wgpu::StringView label, wgpu::BindGroupLayout const& layout,
140144
std::vector<WebGPUDescriptorSetLayout::BindGroupEntryInfo> const& bindGroupEntries);
141145
~WebGPUDescriptorSet();
142146

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

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

0 commit comments

Comments
 (0)