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) {
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) {
@@ -571,7 +582,6 @@ void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
constructHandle<WGPUProgram>(ph, mDevice, program);
}
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
assert_invariant(!mDefaultRenderTarget);
mDefaultRenderTarget = constructHandle<WGPURenderTarget>(rth);
@@ -580,7 +590,21 @@ void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets,
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) {}
@@ -845,35 +869,74 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, RenderPassParams const& params) {
assert_invariant(mCommandEncoder);
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.SetViewport(params.viewport.left, params.viewport.bottom,
params.viewport.width, params.viewport.height, params.depthRange.near, params.depthRange.far);
// Ensure viewport dimensions are positive
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 */) {
@@ -966,10 +1029,20 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
// VulkanPipelineCache to handle this, may be missing nuance
static auto pipleineStateHasher = utils::hash::MurmurHashFn<filament::backend::PipelineState>();
auto hash = pipleineStateHasher(pipelineState);
if(mPipelineMap.find(hash) != mPipelineMap.end()){
if(mPipelineMap.find(hash) != mPipelineMap.end()) {
mRenderPassEncoder.SetPipeline(mPipelineMap[hash]);
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);
assert_invariant(program);
assert_invariant(program->computeShaderModule == nullptr &&
@@ -1012,14 +1085,20 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> 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
// to allow to call this before bindPipeline(), so the validation can only happen in draw()
auto vbi = handleCast<WGPUVertexBufferInfo>(renderPrimitive->vertexBuffer->vbih);
assert_invariant(
vbi->getVertexBufferLayoutSize() == renderPrimitive->vertexBuffer->buffers.size());
for (uint32_t i = 0; i < vbi->getVertexBufferLayoutSize(); i++) {
mRenderPassEncoder.SetVertexBuffer(i, renderPrimitive->vertexBuffer->buffers[i]);
// Loop over the WebGPU vertex buffer slots defined by the VertexBufferInfo.
// For each WebGPU slot, find the corresponding Filament buffer that supplies the data.
for (uint32_t webGPUSlot = 0; webGPUSlot < vbi->getWebGPULayoutCount(); ++webGPUSlot) {
uint8_t filamentBufferIndex = vbi->getFilamentBufferIndexForSlot(webGPUSlot);
// Ensure the Filament buffer index is valid for the vertexBuffer's internal storage.
// 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(),
@@ -1093,9 +1172,12 @@ void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> dsh,
backend::descriptor_set_t setIndex, backend::DescriptorSetOffsetArray&& offsets) {
const auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
const auto wbg = bindGroup->lockAndReturn(mDevice);
assert_invariant(mRenderPassEncoder);
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) {

View File

@@ -72,6 +72,12 @@ private:
wgpu::CommandEncoder mCommandEncoder = nullptr;
wgpu::TextureView mTextureView = 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;
WGPURenderTarget* mDefaultRenderTarget = nullptr;

View File

@@ -25,6 +25,7 @@
#include <algorithm>
#include <cstdint>
#include <map>
#include <utility>
#include <vector>
@@ -196,43 +197,77 @@ void WGPUBufferBase::updateGPUBuffer(BufferDescriptor& bufferDescriptor, uint32_
queue.WriteBuffer(buffer, byteOffset + legalSize, &mRemainderChunk, 4);
}
}
WGPUVertexBufferInfo::WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
mVertexBufferLayout(bufferCount),
mAttributes(bufferCount) {
: HwVertexBufferInfo(bufferCount, attributeCount) { // Base class constructor
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++) {
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) {
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 isNormalized = attrib.flags & Attribute::FLAG_NORMALIZED;
wgpu::VertexFormat vertexFormat = getVertexFormat(attrib.type, isNormalized, isInteger);
// Attributes are sequential per buffer
mAttributes[attrib.buffer].push_back({
mActualWebGPUAttributes[currentWebGPUSlot].push_back({
.format = vertexFormat,
.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++) {
mVertexBufferLayout[bufferIndex].attributeCount = mAttributes[bufferIndex].size();
mVertexBufferLayout[bufferIndex].attributes = mAttributes[bufferIndex].data();
// Finalize attribute pointers and counts in mActualWebGPULayouts
for (uint32_t slot = 0; slot < mActualWebGPULayouts.size(); ++slot) {
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;
}
WGPURenderTarget::Attachment WGPURenderTarget::getDrawColorAttachment(size_t index) {
assert_invariant( index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
auto result = color[index];
if (index == 0 && defaultRenderTarget) {
}
return result;
WGPURenderTarget::WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples,
const MRT& colorAttachmentsMRT,
const Attachment& depthAttachmentInfo,
const Attachment& stencilAttachmentInfo)
: HwRenderTarget(width, height),
defaultRenderTarget(false),
samples(samples),
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,
TargetBufferFlags buffer) {
auto clearFlags = params.flags.clear;
auto discardStartFlags = params.flags.discardStart;
if (any(clearFlags & buffer)) {
return wgpu::LoadOp::Clear;
} else if (any(discardStartFlags & buffer)) {
TargetBufferFlags bufferToOperateOn) {
if (any(params.flags.clear & bufferToOperateOn)) {
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;
}
// Corrected getStoreOperation
wgpu::StoreOp WGPURenderTarget::getStoreOperation(RenderPassParams const& params,
TargetBufferFlags buffer) {
const auto discardEndFlags = params.flags.discardEnd;
if (any(discardEndFlags & buffer)) {
TargetBufferFlags bufferToOperateOn) {
if (any(params.flags.discardEnd & bufferToOperateOn)) {
return wgpu::StoreOp::Discard;
}
return wgpu::StoreOp::Store;
}
void WGPURenderTarget::setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor,
wgpu::TextureView const& textureView, RenderPassParams const& params) {
// auto discardFlags = params.flags.discardEnd;
// (void) discardFlags;
// std::vector<wgpu::RenderPassColorAttachment> colorAttachments;
colorAttachments.clear();
for (size_t i = 0; i < 1/*MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT*/; i++) {
// auto attachment = getDrawColorAttachment(i);
// if (attachment) {
wgpu::RenderPassColorAttachment colorAttachment;
colorAttachment.view = textureView;
colorAttachment.loadOp = getLoadOperation(params, getTargetBufferFlagsAt(i));
colorAttachment.storeOp = getStoreOperation(params, getTargetBufferFlagsAt(i));
colorAttachment.clearValue = { params.clearColor.r, params.clearColor.g, params.clearColor.b, params.clearColor.a };
colorAttachments.emplace_back(colorAttachment);
// }
void WGPURenderTarget::setUpRenderPassAttachments(
wgpu::RenderPassDescriptor& descriptor,
RenderPassParams const& params,
wgpu::TextureView const& defaultColorTextureView,
wgpu::TextureView const& defaultDepthStencilTextureView,
wgpu::TextureView const* customColorTextureViews,
uint32_t customColorTextureViewCount,
wgpu::TextureView const& customDepthTextureView,
wgpu::TextureView const& customStencilTextureView) {
mColorAttachmentDescriptors.clear();
mHasDepthStencilAttachment = false;
if (defaultRenderTarget) {
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.depthStencilAttachment = nullptr;
descriptor.timestampWrites = nullptr;
descriptor.colorAttachmentCount = mColorAttachmentDescriptors.size();
descriptor.colorAttachments = mColorAttachmentDescriptors.data();
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

View File

@@ -52,30 +52,40 @@ class WGPUVertexBufferInfo : public HwVertexBufferInfo {
public:
WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
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 {
return mVertexBufferLayout.size();
// Returns the count of WebGPU native vertex buffer layouts
inline uint32_t getWebGPULayoutCount() const {
return mActualWebGPULayouts.size();
}
inline wgpu::VertexAttribute const* getVertexAttributeForIndex(uint32_t index) const {
assert_invariant(index < mAttributes.size());
return mAttributes[index].data();
}
inline uint32_t getVertexAttributeSize(uint32_t index) const {
assert_invariant(index < mAttributes.size());
return mAttributes[index].size();
// Returns the Filament buffer index for a given WebGPU layout slot
inline uint8_t getFilamentBufferIndexForSlot(uint32_t webgpuSlotIndex) const {
assert_invariant(webgpuSlotIndex < mSlotMappings.size());
return mSlotMappings[webgpuSlotIndex].filamentBufferIndex;
}
private:
// TODO: can we do better in terms on heap management.
std::vector<wgpu::VertexBufferLayout> mVertexBufferLayout{};
std::vector<std::vector<wgpu::VertexAttribute>> mAttributes{};
};
// Stores the final WebGPU vertex buffer layouts
std::vector<wgpu::VertexBufferLayout> mActualWebGPULayouts;
// 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 {
WGPUVertexBuffer(wgpu::Device const &device, uint32_t vertexCount, uint32_t bufferCount,
Handle<HwVertexBufferInfo> vbih);
@@ -197,57 +207,66 @@ struct WGPURenderPrimitive : public HwRenderPrimitive {
WGPUIndexBuffer* indexBuffer = nullptr;
};
class WGPURenderTarget : public HwRenderTarget {
public:
class Attachment {
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;
};
using Attachment = TargetBufferInfo; // Using TargetBufferInfo directly for attachments
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()
: HwRenderTarget(0, 0),
defaultRenderTarget(true) {}
defaultRenderTarget(true),
samples(1) {}
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor,
wgpu::TextureView const& textureView, RenderPassParams const& params);
// Updated signature: takes resolved views for custom RTs, and default views for default RT
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; }
uint8_t getSamples() const { return samples; }
Attachment getDrawColorAttachment(size_t index);
Attachment getReadColorAttachment(size_t index);
// Accessors for the driver to get stored attachment info
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::StoreOp getStoreOperation(const RenderPassParams& params, TargetBufferFlags buffer);
private:
// Helper to map MRT index to TargetBufferFlags
static TargetBufferFlags getTargetBufferFlagsAt(int mrtIndex);
bool defaultRenderTarget = false;
uint8_t samples = 1;
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
math::uint2 attachmentSize = {};
std::vector<wgpu::RenderPassColorAttachment> colorAttachments{};
};
MRT mColorAttachments{};
Attachment mDepthAttachment{};
Attachment mStencilAttachment{};
// Cached descriptors for the render pass
std::vector<wgpu::RenderPassColorAttachment> mColorAttachmentDescriptors;
wgpu::RenderPassDepthStencilAttachment mDepthStencilAttachmentDescriptor{};
bool mHasDepthStencilAttachment = false;
};
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H

View File

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

View File

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

View File

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

View File

@@ -48,7 +48,7 @@
#include <unordered_map>
#include <utility>
#include <vector>
#include "regex"
#include <stddef.h>
#include <stdint.h>
@@ -601,7 +601,6 @@ bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
return false;
#endif
}
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.
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;
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;
#else
slog.i << "Trying to emit WGSL without including WebGPU dependencies,"