Compare commits

...

4 Commits

Author SHA1 Message Date
bridgewaterrobbie
8e2798f861 Needs cleaning: Move descriptor set binding to bind pipeline, caching them if a render pass is not begun 2025-05-23 15:56:33 -04:00
bridgewaterrobbie
f9842fd40b Hack: Use regex to remove tint annotations 2025-05-23 15:55:22 -04:00
bridgewaterrobbie
109908c460 Gemini tinkering 3 2025-05-21 18:02:26 -04:00
bridgewaterrobbie
3a1987406b More gemini tinkering 2025-05-21 15:11:10 -04:00
8 changed files with 385 additions and 147 deletions

View File

@@ -354,6 +354,17 @@ void WebGPUDriver::destroyProgram(Handle<HwProgram> ph) {
} }
void WebGPUDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) { void WebGPUDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
if (rth) {
WGPURenderTarget* rt = handleCast<WGPURenderTarget>(rth);
if (rt == mDefaultRenderTarget) {
mDefaultRenderTarget = nullptr;
}
// WGPURenderTarget destructor is trivial.
// The HwTexture handles stored within WGPURenderTarget (via MRT, TargetBufferInfo)
// are not owned by WGPURenderTarget, so they are not destroyed here.
// They are destroyed via WebGPUDriver::destroyTexture.
destructHandle<WGPURenderTarget>(rth);
}
} }
void WebGPUDriver::destroySwapChain(Handle<HwSwapChain> sch) { void WebGPUDriver::destroySwapChain(Handle<HwSwapChain> sch) {
@@ -571,7 +582,6 @@ void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) { void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
constructHandle<WGPUProgram>(ph, mDevice, program); constructHandle<WGPUProgram>(ph, mDevice, program);
} }
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) { void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
assert_invariant(!mDefaultRenderTarget); assert_invariant(!mDefaultRenderTarget);
mDefaultRenderTarget = constructHandle<WGPURenderTarget>(rth); mDefaultRenderTarget = constructHandle<WGPURenderTarget>(rth);
@@ -580,7 +590,21 @@ void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets, void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets,
uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color, uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color,
TargetBufferInfo depth, TargetBufferInfo stencil) {} TargetBufferInfo depth, TargetBufferInfo stencil) {
// The `targets` flags indicate which of the `color`, `depth`, `stencil` TargetBufferInfo
// are actually active for this render target.
// We'll pass all TargetBufferInfo to WGPURenderTarget; it will use them if their handles are valid.
// Ensure that textures intended for use as attachments were created with
// wgpu::TextureUsage::RenderAttachment. This check should ideally be in createTextureR
// or validated here if possible.
// The `layerCount` parameter might be for creating array textures that this RT targets.
// Individual attachments (color[i].layer, depth.layer, stencil.layer) specify which layer
// of an array texture to bind. For now, we assume textures are pre-configured.
constructHandle<WGPURenderTarget>(rth, width, height, samples, color, depth, stencil);
}
void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {} void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {}
@@ -845,35 +869,74 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, RenderPassParams const& params) { void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, RenderPassParams const& params) {
assert_invariant(mCommandEncoder); assert_invariant(mCommandEncoder);
auto* renderTarget = handleCast<WGPURenderTarget>(rth); auto* renderTarget = handleCast<WGPURenderTarget>(rth);
// if (renderTarget == mDefaultRenderTarget) {
// FWGPU_LOGW << "Default render target"
// << utils::io::endl;
// } else {
// FWGPU_LOGW << "Non Default render target"
// << utils::io::endl;
// }
wgpu::RenderPassDescriptor renderPassDescriptor;
wgpu::RenderPassDepthStencilAttachment depthStencilAttachment{
.view = mSwapChain->getDepthTextureView(),
.depthLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH),
.depthStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH),
.depthClearValue = static_cast<float>(params.clearDepth),
.depthReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0,
.stencilLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL),
.stencilStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL),
.stencilClearValue = params.clearStencil,
.stencilReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0
};
renderTarget->setUpRenderPassAttachments(renderPassDescriptor, mTextureView, params);
renderPassDescriptor.depthStencilAttachment = &depthStencilAttachment;
assert_invariant(mTextureView); wgpu::RenderPassDescriptor renderPassDescriptor{};
wgpu::TextureView defaultColorView = nullptr;
wgpu::TextureView defaultDepthStencilView = nullptr;
std::array<wgpu::TextureView, MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT> customColorViews{};
uint32_t customColorViewCount = 0;
wgpu::TextureView customDepthView = nullptr;
wgpu::TextureView customStencilView = nullptr;
if (renderTarget->isDefaultRenderTarget()) {
assert_invariant(mSwapChain && mTextureView);
defaultColorView = mTextureView;
defaultDepthStencilView = mSwapChain->getDepthTextureView();
} else {
// Resolve views for custom render target
const auto& colorInfos = renderTarget->getColorAttachmentInfos();
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; ++i) {
if (colorInfos[i].handle) {
auto* hwTexture = handleCast<WGPUTexture>(colorInfos[i].handle);
if (hwTexture) {
// TODO: Consider colorInfos[i].level and colorInfos[i].layer for view creation
// if WGPUTexture::getTexView() isn't sufficient or needs parameters.
customColorViews[customColorViewCount++] = hwTexture->getTexView();
}
}
}
const auto& depthInfo = renderTarget->getDepthAttachmentInfo();
if (depthInfo.handle) {
auto* hwTexture = handleCast<WGPUTexture>(depthInfo.handle);
if (hwTexture) {
customDepthView = hwTexture->getTexView();
}
}
const auto& stencilInfo = renderTarget->getStencilAttachmentInfo();
if (stencilInfo.handle) {
// If depth and stencil use the same texture handle, this will re-cast but that's fine.
auto* hwTexture = handleCast<WGPUTexture>(stencilInfo.handle);
if (hwTexture) {
customStencilView = hwTexture->getTexView();
}
}
}
renderTarget->setUpRenderPassAttachments(renderPassDescriptor,
params,
defaultColorView,
defaultDepthStencilView,
customColorViews.data(),
customColorViewCount,
customDepthView,
customStencilView);
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor); mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
mRenderPassEncoder.SetViewport(params.viewport.left, params.viewport.bottom, // Ensure viewport dimensions are positive
params.viewport.width, params.viewport.height, params.depthRange.near, params.depthRange.far); uint32_t viewportWidth = params.viewport.width > 0 ? params.viewport.width : 1;
uint32_t viewportHeight = params.viewport.height > 0 ? params.viewport.height : 1;
mRenderPassEncoder.SetViewport(
static_cast<float>(params.viewport.left),
static_cast<float>(params.viewport.bottom),
static_cast<float>(viewportWidth),
static_cast<float>(viewportHeight),
params.depthRange.near,
params.depthRange.far);
} }
void WebGPUDriver::endRenderPass(int /* dummy */) { void WebGPUDriver::endRenderPass(int /* dummy */) {
@@ -966,10 +1029,20 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
// VulkanPipelineCache to handle this, may be missing nuance // VulkanPipelineCache to handle this, may be missing nuance
static auto pipleineStateHasher = utils::hash::MurmurHashFn<filament::backend::PipelineState>(); static auto pipleineStateHasher = utils::hash::MurmurHashFn<filament::backend::PipelineState>();
auto hash = pipleineStateHasher(pipelineState); auto hash = pipleineStateHasher(pipelineState);
if(mPipelineMap.find(hash) != mPipelineMap.end()){ if(mPipelineMap.find(hash) != mPipelineMap.end()) {
mRenderPassEncoder.SetPipeline(mPipelineMap[hash]); mRenderPassEncoder.SetPipeline(mPipelineMap[hash]);
return; return;
} }
for (size_t index = 0; index < MAX_DESCRIPTOR_SET_COUNT; ++index) {
auto& entry = currentDescriptorSets[index];
if (entry.bg != nullptr) {
mRenderPassEncoder.SetBindGroup(index, entry.bg, entry.dynamicOffsetCount,
entry.offsets.data());
entry.bg = nullptr;
entry.dynamicOffsetCount = 0;
entry.offsets.clear();
}
}
const auto* program = handleCast<WGPUProgram>(pipelineState.program); const auto* program = handleCast<WGPUProgram>(pipelineState.program);
assert_invariant(program); assert_invariant(program);
assert_invariant(program->computeShaderModule == nullptr && assert_invariant(program->computeShaderModule == nullptr &&
@@ -1012,14 +1085,20 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) { void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
auto* renderPrimitive = handleCast<WGPURenderPrimitive>(rph); auto* renderPrimitive = handleCast<WGPURenderPrimitive>(rph);
auto* vbi = handleCast<WGPUVertexBufferInfo>(renderPrimitive->vertexBuffer->vbih);
// This *must* match the WGPUVertexBufferInfo that was bound in bindPipeline(). But we want // Loop over the WebGPU vertex buffer slots defined by the VertexBufferInfo.
// to allow to call this before bindPipeline(), so the validation can only happen in draw() // For each WebGPU slot, find the corresponding Filament buffer that supplies the data.
auto vbi = handleCast<WGPUVertexBufferInfo>(renderPrimitive->vertexBuffer->vbih); for (uint32_t webGPUSlot = 0; webGPUSlot < vbi->getWebGPULayoutCount(); ++webGPUSlot) {
assert_invariant( uint8_t filamentBufferIndex = vbi->getFilamentBufferIndexForSlot(webGPUSlot);
vbi->getVertexBufferLayoutSize() == renderPrimitive->vertexBuffer->buffers.size());
for (uint32_t i = 0; i < vbi->getVertexBufferLayoutSize(); i++) { // Ensure the Filament buffer index is valid for the vertexBuffer's internal storage.
mRenderPassEncoder.SetVertexBuffer(i, renderPrimitive->vertexBuffer->buffers[i]); // This check assumes renderPrimitive->vertexBuffer->buffers is sized by the
// original Filament buffer count.
assert_invariant(filamentBufferIndex < renderPrimitive->vertexBuffer->buffers.size());
mRenderPassEncoder.SetVertexBuffer(webGPUSlot,
renderPrimitive->vertexBuffer->buffers[filamentBufferIndex]);
} }
mRenderPassEncoder.SetIndexBuffer(renderPrimitive->indexBuffer->getBuffer(), mRenderPassEncoder.SetIndexBuffer(renderPrimitive->indexBuffer->getBuffer(),
@@ -1093,9 +1172,12 @@ void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> dsh,
backend::descriptor_set_t setIndex, backend::DescriptorSetOffsetArray&& offsets) { backend::descriptor_set_t setIndex, backend::DescriptorSetOffsetArray&& offsets) {
const auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh); const auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
const auto wbg = bindGroup->lockAndReturn(mDevice); const auto wbg = bindGroup->lockAndReturn(mDevice);
assert_invariant(mRenderPassEncoder);
const size_t dynamicOffsetCount = bindGroup->countEntitiesWithDynamicOffsets(); const size_t dynamicOffsetCount = bindGroup->countEntitiesWithDynamicOffsets();
mRenderPassEncoder.SetBindGroup(setIndex, wbg, dynamicOffsetCount, offsets.data());
if(mRenderPassEncoder){
mRenderPassEncoder.SetBindGroup(setIndex, wbg, dynamicOffsetCount, offsets.data());
}
currentDescriptorSets[setIndex] = {wbg, dynamicOffsetCount, std::move(offsets)};
} }
void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) { void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {

View File

@@ -72,6 +72,12 @@ private:
wgpu::CommandEncoder mCommandEncoder = nullptr; wgpu::CommandEncoder mCommandEncoder = nullptr;
wgpu::TextureView mTextureView = nullptr; wgpu::TextureView mTextureView = nullptr;
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr; wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
struct bgInfo{
wgpu::BindGroup bg;
size_t dynamicOffsetCount;
backend::DescriptorSetOffsetArray offsets;
};
std::array<bgInfo, MAX_DESCRIPTOR_SET_COUNT> currentDescriptorSets = {};
wgpu::CommandBuffer mCommandBuffer = nullptr; wgpu::CommandBuffer mCommandBuffer = nullptr;
WGPURenderTarget* mDefaultRenderTarget = nullptr; WGPURenderTarget* mDefaultRenderTarget = nullptr;

View File

@@ -25,6 +25,7 @@
#include <algorithm> #include <algorithm>
#include <cstdint> #include <cstdint>
#include <map>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -196,43 +197,77 @@ void WGPUBufferBase::updateGPUBuffer(BufferDescriptor& bufferDescriptor, uint32_
queue.WriteBuffer(buffer, byteOffset + legalSize, &mRemainderChunk, 4); queue.WriteBuffer(buffer, byteOffset + legalSize, &mRemainderChunk, 4);
} }
} }
WGPUVertexBufferInfo::WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount, WGPUVertexBufferInfo::WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes) AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount), : HwVertexBufferInfo(bufferCount, attributeCount) { // Base class constructor
mVertexBufferLayout(bufferCount),
mAttributes(bufferCount) {
assert_invariant(attributeCount > 0); assert_invariant(attributeCount > 0);
assert_invariant(bufferCount > 0); // bufferCount is Filament's buffer count. The number of WebGPU slots might differ.
struct LayoutKey {
uint8_t filamentBufferIndex;
uint16_t stride;
bool operator<(const LayoutKey& other) const {
if (filamentBufferIndex != other.filamentBufferIndex) {
return filamentBufferIndex < other.filamentBufferIndex;
}
return stride < other.stride;
}
};
std::map<LayoutKey, uint32_t> layoutKeyToWebGPUSlot;
uint32_t nextWebGPUSlot = 0;
for (uint32_t attribIndex = 0; attribIndex < attributes.size(); attribIndex++) { for (uint32_t attribIndex = 0; attribIndex < attributes.size(); attribIndex++) {
Attribute const& attrib = attributes[attribIndex]; Attribute const& attrib = attributes[attribIndex];
// Ignore the attributes which are not bind to vertex buffers. // Ignore attributes not bound to any vertex buffer.
if (attrib.buffer == Attribute::BUFFER_UNUSED) { if (attrib.buffer == Attribute::BUFFER_UNUSED) {
continue; continue;
} }
assert_invariant(attrib.buffer < bufferCount); assert_invariant(attrib.buffer < bufferCount); // Ensure Filament buffer index is valid
LayoutKey key = {attrib.buffer, attrib.stride};
uint32_t currentWebGPUSlot;
auto it = layoutKeyToWebGPUSlot.find(key);
if (it == layoutKeyToWebGPUSlot.end()) {
currentWebGPUSlot = nextWebGPUSlot++;
layoutKeyToWebGPUSlot[key] = currentWebGPUSlot;
mActualWebGPULayouts.emplace_back();
mActualWebGPULayouts.back().arrayStride = attrib.stride;
mActualWebGPULayouts.back().stepMode = wgpu::VertexStepMode::Vertex;
// .attributes and .attributeCount will be set later
mSlotMappings.emplace_back(SlotMapping{attrib.buffer});
mActualWebGPUAttributes.emplace_back(); // Add a new vector for this slot's attributes
} else {
currentWebGPUSlot = it->second;
// Sanity check: if we found an existing slot for this key, its stride should match.
assert_invariant(mActualWebGPULayouts[currentWebGPUSlot].arrayStride == attrib.stride);
}
bool const isInteger = attrib.flags & Attribute::FLAG_INTEGER_TARGET; bool const isInteger = attrib.flags & Attribute::FLAG_INTEGER_TARGET;
bool const isNormalized = attrib.flags & Attribute::FLAG_NORMALIZED; bool const isNormalized = attrib.flags & Attribute::FLAG_NORMALIZED;
wgpu::VertexFormat vertexFormat = getVertexFormat(attrib.type, isNormalized, isInteger); wgpu::VertexFormat vertexFormat = getVertexFormat(attrib.type, isNormalized, isInteger);
// Attributes are sequential per buffer mActualWebGPUAttributes[currentWebGPUSlot].push_back({
mAttributes[attrib.buffer].push_back({
.format = vertexFormat, .format = vertexFormat,
.offset = attrib.offset, .offset = attrib.offset,
.shaderLocation = attribIndex, .shaderLocation = attribIndex, // Use original attribIndex as shaderLocation
}); });
mVertexBufferLayout[attrib.buffer].stepMode = wgpu::VertexStepMode::Vertex;
if (mVertexBufferLayout[attrib.buffer].arrayStride == 0) {
mVertexBufferLayout[attrib.buffer].arrayStride = attrib.stride;
} else {
assert_invariant(mVertexBufferLayout[attrib.buffer].arrayStride == attrib.stride);
}
} }
for (uint32_t bufferIndex = 0; bufferIndex < bufferCount; bufferIndex++) { // Finalize attribute pointers and counts in mActualWebGPULayouts
mVertexBufferLayout[bufferIndex].attributeCount = mAttributes[bufferIndex].size(); for (uint32_t slot = 0; slot < mActualWebGPULayouts.size(); ++slot) {
mVertexBufferLayout[bufferIndex].attributes = mAttributes[bufferIndex].data(); if (!mActualWebGPUAttributes[slot].empty()) {
mActualWebGPULayouts[slot].attributeCount = mActualWebGPUAttributes[slot].size();
mActualWebGPULayouts[slot].attributes = mActualWebGPUAttributes[slot].data();
} else {
mActualWebGPULayouts[slot].attributeCount = 0;
mActualWebGPULayouts[slot].attributes = nullptr;
}
} }
} }
@@ -923,58 +958,147 @@ wgpu::TextureView WGPUTexture::makeTextureView(const uint8_t& baseLevel, const u
return textureView; return textureView;
} }
WGPURenderTarget::Attachment WGPURenderTarget::getDrawColorAttachment(size_t index) { WGPURenderTarget::WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples,
assert_invariant( index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT); const MRT& colorAttachmentsMRT,
auto result = color[index]; const Attachment& depthAttachmentInfo,
if (index == 0 && defaultRenderTarget) { const Attachment& stencilAttachmentInfo)
: HwRenderTarget(width, height),
} defaultRenderTarget(false),
samples(samples),
return result; mColorAttachments(colorAttachmentsMRT),
mDepthAttachment(depthAttachmentInfo),
mStencilAttachment(stencilAttachmentInfo) {
mColorAttachmentDescriptors.reserve(MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
} }
// Static helper to map MRT index to TargetBufferFlags
TargetBufferFlags WGPURenderTarget::getTargetBufferFlagsAt(int mrtIndex) {
if (mrtIndex < 0 || mrtIndex >= MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT) {
return TargetBufferFlags::NONE;
}
// This mapping assumes TargetBufferFlags::COLOR_0, COLOR_1 etc. are contiguous
return static_cast<TargetBufferFlags>(
static_cast<uint32_t>(TargetBufferFlags::COLOR0) << mrtIndex);
}
// Corrected getLoadOperation
wgpu::LoadOp WGPURenderTarget::getLoadOperation(RenderPassParams const& params, wgpu::LoadOp WGPURenderTarget::getLoadOperation(RenderPassParams const& params,
TargetBufferFlags buffer) { TargetBufferFlags bufferToOperateOn) {
auto clearFlags = params.flags.clear; if (any(params.flags.clear & bufferToOperateOn)) {
auto discardStartFlags = params.flags.discardStart;
if (any(clearFlags & buffer)) {
return wgpu::LoadOp::Clear;
} else if (any(discardStartFlags & buffer)) {
return wgpu::LoadOp::Clear; return wgpu::LoadOp::Clear;
} }
if (any(params.flags.discardStart & bufferToOperateOn)) {
return wgpu::LoadOp::Clear; // Or wgpu::LoadOp::Undefined if clear is not desired on discard
}
return wgpu::LoadOp::Load; return wgpu::LoadOp::Load;
} }
// Corrected getStoreOperation
wgpu::StoreOp WGPURenderTarget::getStoreOperation(RenderPassParams const& params, wgpu::StoreOp WGPURenderTarget::getStoreOperation(RenderPassParams const& params,
TargetBufferFlags buffer) { TargetBufferFlags bufferToOperateOn) {
const auto discardEndFlags = params.flags.discardEnd; if (any(params.flags.discardEnd & bufferToOperateOn)) {
if (any(discardEndFlags & buffer)) {
return wgpu::StoreOp::Discard; return wgpu::StoreOp::Discard;
} }
return wgpu::StoreOp::Store; return wgpu::StoreOp::Store;
} }
void WGPURenderTarget::setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor,
wgpu::TextureView const& textureView, RenderPassParams const& params) { void WGPURenderTarget::setUpRenderPassAttachments(
// auto discardFlags = params.flags.discardEnd; wgpu::RenderPassDescriptor& descriptor,
// (void) discardFlags; RenderPassParams const& params,
// std::vector<wgpu::RenderPassColorAttachment> colorAttachments; wgpu::TextureView const& defaultColorTextureView,
colorAttachments.clear(); wgpu::TextureView const& defaultDepthStencilTextureView,
for (size_t i = 0; i < 1/*MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT*/; i++) { wgpu::TextureView const* customColorTextureViews,
// auto attachment = getDrawColorAttachment(i); uint32_t customColorTextureViewCount,
// if (attachment) { wgpu::TextureView const& customDepthTextureView,
wgpu::RenderPassColorAttachment colorAttachment; wgpu::TextureView const& customStencilTextureView) {
colorAttachment.view = textureView; mColorAttachmentDescriptors.clear();
colorAttachment.loadOp = getLoadOperation(params, getTargetBufferFlagsAt(i)); mHasDepthStencilAttachment = false;
colorAttachment.storeOp = getStoreOperation(params, getTargetBufferFlagsAt(i));
colorAttachment.clearValue = { params.clearColor.r, params.clearColor.g, params.clearColor.b, params.clearColor.a }; if (defaultRenderTarget) {
colorAttachments.emplace_back(colorAttachment); assert_invariant(defaultColorTextureView);
// } wgpu::RenderPassColorAttachment colorAttDesc{};
colorAttDesc.view = defaultColorTextureView;
colorAttDesc.resolveTarget = nullptr;
colorAttDesc.loadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::COLOR0);
colorAttDesc.storeOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::COLOR0);
colorAttDesc.clearValue = {params.clearColor.r, params.clearColor.g,
params.clearColor.b, params.clearColor.a};
mColorAttachmentDescriptors.push_back(colorAttDesc);
if (defaultDepthStencilTextureView) {
mDepthStencilAttachmentDescriptor = {
.view = defaultDepthStencilTextureView,
.depthLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH),
.depthStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH),
.depthClearValue = static_cast<float>(params.clearDepth),
.depthReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0,
.stencilLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL),
.stencilStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL),
.stencilClearValue = params.clearStencil,
.stencilReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0,
};
mHasDepthStencilAttachment = true;
}
} else { // Custom Render Target
for (uint32_t i = 0; i < customColorTextureViewCount; ++i) {
if (customColorTextureViews[i]) {
wgpu::RenderPassColorAttachment colorAttDesc{};
colorAttDesc.view = customColorTextureViews[i];
colorAttDesc.resolveTarget = nullptr; // TODO: MSAA resolve for custom RT
colorAttDesc.loadOp = WGPURenderTarget::getLoadOperation(params, getTargetBufferFlagsAt(i));
colorAttDesc.storeOp = WGPURenderTarget::getStoreOperation(params, getTargetBufferFlagsAt(i));
colorAttDesc.clearValue = {params.clearColor.r, params.clearColor.g,
params.clearColor.b, params.clearColor.a};
mColorAttachmentDescriptors.push_back(colorAttDesc);
}
}
wgpu::TextureView combinedDsView = customDepthTextureView ? customDepthTextureView : customStencilTextureView;
if (combinedDsView) {
mDepthStencilAttachmentDescriptor = {};
mDepthStencilAttachmentDescriptor.view = combinedDsView;
if (customDepthTextureView) {
mDepthStencilAttachmentDescriptor.depthLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH);
mDepthStencilAttachmentDescriptor.depthStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH);
mDepthStencilAttachmentDescriptor.depthClearValue = static_cast<float>(params.clearDepth);
mDepthStencilAttachmentDescriptor.depthReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0;
} else {
mDepthStencilAttachmentDescriptor.depthLoadOp = wgpu::LoadOp::Undefined;
mDepthStencilAttachmentDescriptor.depthStoreOp = wgpu::StoreOp::Undefined;
mDepthStencilAttachmentDescriptor.depthReadOnly = true;
}
if (customStencilTextureView) {
mDepthStencilAttachmentDescriptor.stencilLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL);
mDepthStencilAttachmentDescriptor.stencilStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL);
mDepthStencilAttachmentDescriptor.stencilClearValue = params.clearStencil;
mDepthStencilAttachmentDescriptor.stencilReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0;
} else {
mDepthStencilAttachmentDescriptor.stencilLoadOp = wgpu::LoadOp::Undefined;
mDepthStencilAttachmentDescriptor.stencilStoreOp = wgpu::StoreOp::Undefined;
mDepthStencilAttachmentDescriptor.stencilReadOnly = true;
}
mHasDepthStencilAttachment = true;
}
} }
descriptor.colorAttachments = colorAttachments.data();
descriptor.colorAttachmentCount = colorAttachments.size(); descriptor.colorAttachmentCount = mColorAttachmentDescriptors.size();
descriptor.depthStencilAttachment = nullptr; descriptor.colorAttachments = mColorAttachmentDescriptors.data();
descriptor.timestampWrites = nullptr; descriptor.depthStencilAttachment = mHasDepthStencilAttachment ? &mDepthStencilAttachmentDescriptor : nullptr;
// descriptor.sampleCount was removed from the core spec. If your webgpu.h still has it,
// and your Dawn version expects it, you might need to set it here based on this->samples.
// e.g., descriptor.sampleCount = this->samples;
} }
math::uint2 WGPURenderTarget::getAttachmentSize() const noexcept {
if (!defaultRenderTarget) {
return {width, height};
}
// For default RT, size is dynamic and usually obtained from the swapchain.
// The caller (WebGPUDriver::beginRenderPass) should know the current swapchain size.
return {0,0};
}
}// namespace filament::backend }// namespace filament::backend

View File

@@ -52,30 +52,40 @@ class WGPUVertexBufferInfo : public HwVertexBufferInfo {
public: public:
WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount, WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes); AttributeArray const& attributes);
inline wgpu::VertexBufferLayout const* getVertexBufferLayout() const {
return mVertexBufferLayout.data(); // Returns the WebGPU native vertex buffer layouts
inline wgpu::VertexBufferLayout const* getWebGPULayouts() const {
return mActualWebGPULayouts.data();
} }
inline uint32_t getVertexBufferLayoutSize() const { // Returns the count of WebGPU native vertex buffer layouts
return mVertexBufferLayout.size(); inline uint32_t getWebGPULayoutCount() const {
return mActualWebGPULayouts.size();
} }
inline wgpu::VertexAttribute const* getVertexAttributeForIndex(uint32_t index) const { // Returns the Filament buffer index for a given WebGPU layout slot
assert_invariant(index < mAttributes.size()); inline uint8_t getFilamentBufferIndexForSlot(uint32_t webgpuSlotIndex) const {
return mAttributes[index].data(); assert_invariant(webgpuSlotIndex < mSlotMappings.size());
} return mSlotMappings[webgpuSlotIndex].filamentBufferIndex;
inline uint32_t getVertexAttributeSize(uint32_t index) const {
assert_invariant(index < mAttributes.size());
return mAttributes[index].size();
} }
private: private:
// TODO: can we do better in terms on heap management. // Stores the final WebGPU vertex buffer layouts
std::vector<wgpu::VertexBufferLayout> mVertexBufferLayout{}; std::vector<wgpu::VertexBufferLayout> mActualWebGPULayouts;
std::vector<std::vector<wgpu::VertexAttribute>> mAttributes{}; // Stores attributes per WebGPU layout, parallel to mActualWebGPULayouts.
}; // Each wgpu::VertexBufferLayout in mActualWebGPULayouts will point to its attributes here.
std::vector<std::vector<wgpu::VertexAttribute>> mActualWebGPUAttributes;
struct SlotMapping {
uint8_t filamentBufferIndex; // Original Filament buffer index for this WebGPU slot
};
// Maps a WebGPU slot index to its original Filament buffer index
std::vector<SlotMapping> mSlotMappings;
// Remove or comment out old members if they were similarly named:
// std::vector<wgpu::VertexBufferLayout> mVertexBufferLayout {}; // OLD
// std::vector<std::vector<wgpu::VertexAttribute>> mAttributes {}; // OLD
};
struct WGPUVertexBuffer : public HwVertexBuffer { struct WGPUVertexBuffer : public HwVertexBuffer {
WGPUVertexBuffer(wgpu::Device const &device, uint32_t vertexCount, uint32_t bufferCount, WGPUVertexBuffer(wgpu::Device const &device, uint32_t vertexCount, uint32_t bufferCount,
Handle<HwVertexBufferInfo> vbih); Handle<HwVertexBufferInfo> vbih);
@@ -197,57 +207,66 @@ struct WGPURenderPrimitive : public HwRenderPrimitive {
WGPUIndexBuffer* indexBuffer = nullptr; WGPUIndexBuffer* indexBuffer = nullptr;
}; };
class WGPURenderTarget : public HwRenderTarget { class WGPURenderTarget : public HwRenderTarget {
public: public:
class Attachment { using Attachment = TargetBufferInfo; // Using TargetBufferInfo directly for attachments
public:
friend class WGPURenderTarget;
Attachment() = default;
Attachment(WGPUTexture* gpuTexture, uint8_t level = 0, uint16_t layer = 0)
: level(level),
layer(layer),
texture(gpuTexture->getTexture()),
mWGPUTexture(gpuTexture) {}
operator bool() const {
return mWGPUTexture != nullptr;
}
uint8_t level = 0;
uint16_t layer = 0;
private:
wgpu::Texture texture = nullptr;
WGPUTexture* mWGPUTexture = nullptr;
};
WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples, WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples,
Attachment colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT]); const MRT& colorAttachments,
const Attachment& depthAttachment,
const Attachment& stencilAttachment);
// Default constructor for the default render target
WGPURenderTarget() WGPURenderTarget()
: HwRenderTarget(0, 0), : HwRenderTarget(0, 0),
defaultRenderTarget(true) {} defaultRenderTarget(true),
samples(1) {}
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor, // Updated signature: takes resolved views for custom RTs, and default views for default RT
wgpu::TextureView const& textureView, RenderPassParams const& params); void setUpRenderPassAttachments(
wgpu::RenderPassDescriptor& descriptor,
RenderPassParams const& params,
// For default render target:
wgpu::TextureView const& defaultColorTextureView,
wgpu::TextureView const& defaultDepthStencilTextureView,
// For custom render targets:
wgpu::TextureView const* customColorTextureViews, // Array of views
uint32_t customColorTextureViewCount,
wgpu::TextureView const& customDepthTextureView,
wgpu::TextureView const& customStencilTextureView);
math::uint2 getAttachmentSize() noexcept;
math::uint2 getAttachmentSize() const noexcept;
bool isDefaultRenderTarget() const { return defaultRenderTarget; } bool isDefaultRenderTarget() const { return defaultRenderTarget; }
uint8_t getSamples() const { return samples; } uint8_t getSamples() const { return samples; }
Attachment getDrawColorAttachment(size_t index); // Accessors for the driver to get stored attachment info
Attachment getReadColorAttachment(size_t index); const MRT& getColorAttachmentInfos() const { return mColorAttachments; }
const Attachment& getDepthAttachmentInfo() const { return mDepthAttachment; }
const Attachment& getStencilAttachmentInfo() const { return mStencilAttachment; }
// Static helpers for load/store operations
static wgpu::LoadOp getLoadOperation(const RenderPassParams& params, TargetBufferFlags buffer); static wgpu::LoadOp getLoadOperation(const RenderPassParams& params, TargetBufferFlags buffer);
static wgpu::StoreOp getStoreOperation(const RenderPassParams& params, TargetBufferFlags buffer); static wgpu::StoreOp getStoreOperation(const RenderPassParams& params, TargetBufferFlags buffer);
private: private:
// Helper to map MRT index to TargetBufferFlags
static TargetBufferFlags getTargetBufferFlagsAt(int mrtIndex);
bool defaultRenderTarget = false; bool defaultRenderTarget = false;
uint8_t samples = 1; uint8_t samples = 1;
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {}; MRT mColorAttachments{};
math::uint2 attachmentSize = {}; Attachment mDepthAttachment{};
std::vector<wgpu::RenderPassColorAttachment> colorAttachments{}; Attachment mStencilAttachment{};
};
// Cached descriptors for the render pass
std::vector<wgpu::RenderPassColorAttachment> mColorAttachmentDescriptors;
wgpu::RenderPassDepthStencilAttachment mDepthStencilAttachmentDescriptor{};
bool mHasDepthStencilAttachment = false;
};
}// namespace filament::backend }// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H #endif// TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H

View File

@@ -191,8 +191,8 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
.entryPoint = "main", .entryPoint = "main",
.constantCount = program.constants.size(), .constantCount = program.constants.size(),
.constants = program.constants.data(), .constants = program.constants.data(),
.bufferCount = vertexBufferInfo.getVertexBufferLayoutSize(), .bufferCount = vertexBufferInfo.getWebGPULayoutCount(),
.buffers = vertexBufferInfo.getVertexBufferLayout() .buffers = vertexBufferInfo.getWebGPULayouts()
}, },
.primitive = { .primitive = {
.topology = toWebGPU(primitiveType), .topology = toWebGPU(primitiveType),

View File

@@ -320,11 +320,14 @@ wgpu::TextureView WebGPUSwapChain::getCurrentSurfaceTextureView(
if (surfaceTexture.status != wgpu::SurfaceGetCurrentTextureStatus::SuccessOptimal) { if (surfaceTexture.status != wgpu::SurfaceGetCurrentTextureStatus::SuccessOptimal) {
return nullptr; return nullptr;
} }
mCurrentSurfaceTexture = nullptr;
mCurrentSurfaceTexture = surfaceTexture.texture;
// Create a view for this surface texture // Create a view for this surface texture
// TODO: review these initiliazations as webgpu pipeline gets mature // TODO: review these initiliazations as webgpu pipeline gets mature
wgpu::TextureViewDescriptor textureViewDescriptor = { wgpu::TextureViewDescriptor textureViewDescriptor = {
.label = "surface_texture_view", .label = "surface_texture_view",
.format = surfaceTexture.texture.GetFormat(), .format = mCurrentSurfaceTexture.GetFormat(),
.dimension = wgpu::TextureViewDimension::e2D, .dimension = wgpu::TextureViewDimension::e2D,
.baseMipLevel = 0, .baseMipLevel = 0,
.mipLevelCount = 1, .mipLevelCount = 1,
@@ -337,6 +340,7 @@ wgpu::TextureView WebGPUSwapChain::getCurrentSurfaceTextureView(
void WebGPUSwapChain::present() { void WebGPUSwapChain::present() {
assert_invariant(mSurface); assert_invariant(mSurface);
mSurface.Present(); mSurface.Present();
mCurrentSurfaceTexture = nullptr;
} }
}// namespace filament::backend }// namespace filament::backend

View File

@@ -52,6 +52,8 @@ private:
wgpu::TextureFormat mDepthFormat = wgpu::TextureFormat::Undefined; wgpu::TextureFormat mDepthFormat = wgpu::TextureFormat::Undefined;
wgpu::Texture mDepthTexture = nullptr; wgpu::Texture mDepthTexture = nullptr;
wgpu::TextureView mDepthTextureView = nullptr; wgpu::TextureView mDepthTextureView = nullptr;
wgpu::Texture mCurrentSurfaceTexture = nullptr; // To hold the texture from getCurrentTexture()
}; };
} // namespace filament::backend } // namespace filament::backend

View File

@@ -48,7 +48,7 @@
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "regex"
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
@@ -601,7 +601,6 @@ bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
return false; return false;
#endif #endif
} }
tint::Result<tint::wgsl::writer::Output> wgslOut = tint::wgsl::writer::Generate(tintRead,writerOpts); tint::Result<tint::wgsl::writer::Output> wgslOut = tint::wgsl::writer::Generate(tintRead,writerOpts);
/// An instance of SuccessType that can be used to check a tint Result. /// An instance of SuccessType that can be used to check a tint Result.
tint::SuccessType tintSuccess; tint::SuccessType tintSuccess;
@@ -610,7 +609,9 @@ bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
slog.e << "Tint writer error: " << wgslOut.Failure().reason << io::endl; slog.e << "Tint writer error: " << wgslOut.Failure().reason << io::endl;
return false; return false;
} }
*outWsl = wgslOut->wgsl; //Tint adds some annotations that Dawn complains about and needs removal
std::regex stride_regex(R"(@stride\([0-9]+\)\s*@internal\(disable_validation__ignore_stride\))");
*outWsl = std::regex_replace(wgslOut->wgsl, stride_regex, "");
return true; return true;
#else #else
slog.i << "Trying to emit WGSL without including WebGPU dependencies," slog.i << "Trying to emit WGSL without including WebGPU dependencies,"