diff --git a/filament/backend/src/webgpu/WebGPUDriver.cpp b/filament/backend/src/webgpu/WebGPUDriver.cpp index 19202742fc..d4d9797fe4 100644 --- a/filament/backend/src/webgpu/WebGPUDriver.cpp +++ b/filament/backend/src/webgpu/WebGPUDriver.cpp @@ -174,6 +174,20 @@ void WebGPUDriver::destroyProgram(Handle ph) { } void WebGPUDriver::destroyRenderTarget(Handle rth) { + if (rth) { + WGPURenderTarget* rt = handleCast(rth); + if (rt == mDefaultRenderTarget) { + mDefaultRenderTarget = nullptr; + } + if (rt == mCurrentRenderTarget) { + mCurrentRenderTarget = 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(rth); + } } void WebGPUDriver::destroySwapChain(Handle sch) { @@ -413,7 +427,21 @@ void WebGPUDriver::createDefaultRenderTargetR(Handle rth, int) { void WebGPUDriver::createRenderTargetR(Handle 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(rth, width, height, samples, color, depth, stencil); +} void WebGPUDriver::createFenceR(Handle fh, int) {} @@ -678,48 +706,80 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority, void WebGPUDriver::beginRenderPass(Handle rth, RenderPassParams const& params) { assert_invariant(mCommandEncoder); - auto* renderTarget = handleCast(rth); - // if (renderTarget == mDefaultRenderTarget) { - // FWGPU_LOGW << "Default render target" - // << utils::io::endl; - // } else { - // FWGPU_LOGW << "Non Default render target" - // << utils::io::endl; - // } - wgpu::TextureView depthTextureView = mSwapChain->getDepthTextureView(); - wgpu::TextureFormat depthTextureFormat = mSwapChain->getDepthFormat(); - wgpu::LoadOp stencilLoadOpValue; - wgpu::StoreOp stencilStoreOpValue; - if (depthTextureFormat == wgpu::TextureFormat::Depth24PlusStencil8 || - depthTextureFormat == wgpu::TextureFormat::Depth32FloatStencil8 - ) { - stencilLoadOpValue = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL); - stencilStoreOpValue = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL); + wgpu::TextureView defaultColorView = nullptr; + wgpu::TextureView defaultDepthStencilView = nullptr; + + std::array customColorViews{}; + uint32_t customColorViewCount = 0; + wgpu::TextureView customDepthView = nullptr; + wgpu::TextureFormat customDepthFormat = wgpu::TextureFormat::Undefined; + wgpu::TextureFormat customStencilFormat = wgpu::TextureFormat::Undefined; + wgpu::TextureView customStencilView = nullptr; + + mCurrentRenderTarget = renderTarget; + if (renderTarget->isDefaultRenderTarget()) { + assert_invariant(mSwapChain && mTextureView); + defaultColorView = mTextureView; + defaultDepthStencilView = mSwapChain->getDepthTextureView(); } else { - stencilLoadOpValue = wgpu::LoadOp::Undefined; - stencilStoreOpValue = wgpu::StoreOp::Undefined; + // 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(colorInfos[i].handle); + if (hwTexture) { + // TODO: Consider colorInfos[i].level and colorInfos[i].layer for view creation + // if WGPUTexture::getTextureView() isn't sufficient or needs parameters. + customColorViews[customColorViewCount++] = hwTexture->getTextureView(); + } + } + } + + const auto& depthInfo = renderTarget->getDepthAttachmentInfo(); + if (depthInfo.handle) { + auto* hwTexture = handleCast(depthInfo.handle); + if (hwTexture) { + customDepthView = hwTexture->getTextureView(); + customDepthFormat = hwTexture->getFormat(); + } + } + + 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(stencilInfo.handle); + if (hwTexture) { + customStencilView = hwTexture->getTextureView(); + customStencilFormat = hwTexture->getFormat(); + } + } } - - wgpu::RenderPassDescriptor renderPassDescriptor; - wgpu::RenderPassDepthStencilAttachment depthStencilAttachment{ .view = depthTextureView, - .depthLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH), - .depthStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH), - .depthClearValue = static_cast(params.clearDepth), - .depthReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0, - .stencilLoadOp = stencilLoadOpValue, - .stencilStoreOp = stencilStoreOpValue, - .stencilClearValue = params.clearStencil, - .stencilReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0 }; - renderTarget->setUpRenderPassAttachments(renderPassDescriptor, mTextureView, params); - renderPassDescriptor.depthStencilAttachment = &depthStencilAttachment; - - assert_invariant(mTextureView); + wgpu::RenderPassDescriptor renderPassDescriptor{}; + renderTarget->setUpRenderPassAttachments(renderPassDescriptor, + params, + defaultColorView, + defaultDepthStencilView, + customColorViews.data(), + customColorViewCount, + customDepthView, + customStencilView, + customDepthFormat, + customStencilFormat); mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor); - mRenderPassEncoder.SetViewport(params.viewport.left, params.viewport.bottom, - params.viewport.width, params.viewport.height, params.depthRange.near, + + // Ensure viewport dimensions are not 0 + FILAMENT_CHECK_POSTCONDITION(params.viewport.width > 0) << "viewport width is 0?"; + FILAMENT_CHECK_POSTCONDITION(params.viewport.height > 0) << "viewport height is 0?"; + + mRenderPassEncoder.SetViewport( + static_cast(params.viewport.left), + static_cast(params.viewport.bottom), + static_cast(params.viewport.width), + static_cast(params.viewport.height), + params.depthRange.near, params.depthRange.far); } @@ -813,7 +873,7 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) { // VulkanPipelineCache to handle this, may be missing nuance static auto pipleineStateHasher = utils::hash::MurmurHashFn(); auto hash = pipleineStateHasher(pipelineState); - if(mPipelineMap.find(hash) != mPipelineMap.end()){ + if (mPipelineMap.find(hash) != mPipelineMap.end()) { mRenderPassEncoder.SetPipeline(mPipelineMap[hash]); return; } @@ -849,10 +909,56 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) { << layoutDescriptor.label; auto const* vertexBufferInfo = handleCast(pipelineState.vertexBufferInfo); assert_invariant(vertexBufferInfo); - const wgpu::RenderPipeline pipeline = createWebGPURenderPipeline(mDevice, *program, - *vertexBufferInfo, layout, pipelineState.rasterState, pipelineState.stencilState, - pipelineState.polygonOffset, pipelineState.primitiveType, mSwapChain->getColorFormat(), - mSwapChain->getDepthFormat()); + + std::vector pipelineColorFormats; + wgpu::TextureFormat pipelineDepthFormat = wgpu::TextureFormat::Undefined; + uint8_t pipelineSamples = 1; + + if (mCurrentRenderTarget->isDefaultRenderTarget()) { + pipelineColorFormats.push_back(mSwapChain->getColorFormat()); + pipelineDepthFormat = mSwapChain->getDepthFormat(); + pipelineSamples = + mCurrentRenderTarget->getSamples();// Default RT should have samples (usually 1) + } else { + const auto& mrtColorAttachments = mCurrentRenderTarget->getColorAttachmentInfos(); + for (size_t i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; ++i) { + if (mrtColorAttachments[i].handle) { + const auto* colorTexture = handleCast(mrtColorAttachments[i].handle); + if (colorTexture) { + pipelineColorFormats.push_back(colorTexture->getTexture().GetFormat()); + } + } + } + + const auto& depthInfo = mCurrentRenderTarget->getDepthAttachmentInfo(); + const auto& stencilInfo = mCurrentRenderTarget->getStencilAttachmentInfo(); + if (depthInfo.handle) { + FILAMENT_CHECK_POSTCONDITION(!stencilInfo.handle) + << "depth and stencil attachments cannot both be provided for WebGPU"; + const auto* dfTexture = handleCast(depthInfo.handle); + if (dfTexture) pipelineDepthFormat = dfTexture->getTexture().GetFormat(); + } else { + if (stencilInfo.handle) { + const auto* sfTexture = handleCast(stencilInfo.handle); + // Assuming combined depth/stencil format if only stencil is present + if (sfTexture) { + pipelineDepthFormat = sfTexture->getTexture().GetFormat(); + } + } + } + pipelineSamples = mCurrentRenderTarget->getSamples(); + } + + if (program->fragmentShaderModule != nullptr) { + FILAMENT_CHECK_POSTCONDITION(!pipelineColorFormats.empty()) + << "Render pipeline with fragment shader must have at least one color target " + "format."; + } + wgpu::RenderPipeline pipeline = createWebGPURenderPipeline(mDevice, *program, *vertexBufferInfo, + layout, pipelineState.rasterState, pipelineState.stencilState, + pipelineState.polygonOffset, pipelineState.primitiveType, pipelineColorFormats, + pipelineDepthFormat, pipelineSamples); + assert_invariant(pipeline); mPipelineMap[hash] = pipeline; mRenderPassEncoder.SetPipeline(pipeline); } @@ -883,13 +989,15 @@ void WebGPUDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 0); } -void WebGPUDriver::draw(PipelineState pipelineState, Handle rph, uint32_t indexOffset, - uint32_t indexCount, uint32_t instanceCount) { - WGPURenderPrimitive const* const renderPrimitive = handleCast(rph); +void WebGPUDriver::draw(PipelineState pipelineState, + Handle renderPrimitiveHandle, uint32_t indexOffset, uint32_t indexCount, + uint32_t instanceCount) { + WGPURenderPrimitive const* const renderPrimitive = + handleCast(renderPrimitiveHandle); pipelineState.primitiveType = renderPrimitive->type; pipelineState.vertexBufferInfo = renderPrimitive->vertexBuffer->vbih; bindPipeline(pipelineState); - bindRenderPrimitive(rph); + bindRenderPrimitive(renderPrimitiveHandle); draw2(indexOffset, indexCount, instanceCount); } @@ -938,7 +1046,7 @@ void WebGPUDriver::updateDescriptorSetTexture(Handle dsh, auto sampler = makeSampler(params); // TODO making assumptions that size and offset mean the same thing here. wgpu::BindGroupEntry tEntry{ .binding = static_cast(binding * 2), - .textureView = texture->getTexView() }; + .textureView = texture->getTextureView() }; bindGroup->addEntry(tEntry.binding, std::move(tEntry)); wgpu::BindGroupEntry sEntry{ .binding = static_cast(binding * 2 + 1), @@ -960,6 +1068,7 @@ void WebGPUDriver::bindDescriptorSet(Handle dsh, void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) { } + wgpu::Sampler WebGPUDriver::makeSampler(SamplerParams const& params) { wgpu::SamplerDescriptor desc{}; diff --git a/filament/backend/src/webgpu/WebGPUDriver.h b/filament/backend/src/webgpu/WebGPUDriver.h index f98cc2316f..f1e6087825 100644 --- a/filament/backend/src/webgpu/WebGPUDriver.h +++ b/filament/backend/src/webgpu/WebGPUDriver.h @@ -74,6 +74,7 @@ private: wgpu::RenderPassEncoder mRenderPassEncoder = nullptr; wgpu::CommandBuffer mCommandBuffer = nullptr; WGPURenderTarget* mDefaultRenderTarget = nullptr; + WGPURenderTarget* mCurrentRenderTarget = nullptr; tsl::robin_map mPipelineMap; diff --git a/filament/backend/src/webgpu/WebGPUHandles.cpp b/filament/backend/src/webgpu/WebGPUHandles.cpp index a4d144d0ad..a2b6e8cc45 100644 --- a/filament/backend/src/webgpu/WebGPUHandles.cpp +++ b/filament/backend/src/webgpu/WebGPUHandles.cpp @@ -592,7 +592,7 @@ WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat forma FILAMENT_CHECK_POSTCONDITION(mTexture) << "Failed to create texture for " << textureDescriptor.label; // Second, the texture view aspect - mTexView = makeTextureView(0, levels, target); + mTexureView = makeTextureView(0, levels, target); } WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept { @@ -601,7 +601,7 @@ WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount mBlockWidth = src->mBlockWidth; mBlockHeight = src->mBlockHeight; - mTexView = makeTextureView(baseLevel, levelCount, target); + mTexureView = makeTextureView(baseLevel, levelCount, target); } wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(TextureUsage const& fUsage) { @@ -970,58 +970,153 @@ 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) { + // TODO Make this an array + mColorAttachmentDescriptors.reserve(MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT); } 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; } 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 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); - // } + 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, wgpu::TextureFormat customDepthFormat, + wgpu::TextureFormat customStencilFormat) { + mColorAttachmentDescriptors.clear(); + mHasDepthStencilAttachment = false; + + if (defaultRenderTarget) { + assert_invariant(defaultColorTextureView); + mColorAttachmentDescriptors.push_back({ .view = defaultColorTextureView, + .resolveTarget = nullptr, + .loadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::COLOR0), + .storeOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::COLOR0), + .clearValue = { params.clearColor.r, params.clearColor.g, params.clearColor.b, + params.clearColor.a } }); + + if (defaultDepthStencilTextureView) { + mDepthStencilAttachmentDescriptor = { + .view = defaultDepthStencilTextureView, + .depthLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH), + .depthStoreOp = + WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH), + .depthClearValue = static_cast(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]) { + mColorAttachmentDescriptors.push_back({ .view = customColorTextureViews[i], + // .resolveTarget = nullptr; // TODO: MSAA resolve for custom RT + .loadOp = WGPURenderTarget::getLoadOperation(params, getTargetBufferFlagsAt(i)), + .storeOp = + WGPURenderTarget::getStoreOperation(params, getTargetBufferFlagsAt(i)), + .clearValue = { .r = params.clearColor.r, + .g = params.clearColor.g, + .b = params.clearColor.b, + .a = params.clearColor.a } }); + } + } + + FILAMENT_CHECK_POSTCONDITION(!(customDepthTextureView && customStencilTextureView)) + << "WebGPU CANNOT support separate texture views for depth + stencil. depth + " + "stencil needs to be in one texture view"; + + const bool hasStencil = + customStencilTextureView || + (customDepthFormat == wgpu::TextureFormat::Depth24PlusStencil8 || + customDepthFormat == wgpu::TextureFormat::Depth32FloatStencil8); + + const bool hasDepth = + customDepthTextureView || + (customStencilFormat == wgpu::TextureFormat::Depth24PlusStencil8 || + customDepthFormat == wgpu::TextureFormat::Depth32FloatStencil8); + + if (customDepthTextureView || customStencilTextureView) { + assert_invariant((hasDepth || hasStencil) && + "Depth or Texture view without a valid texture format"); + mDepthStencilAttachmentDescriptor = {}; + mDepthStencilAttachmentDescriptor.view = + customDepthTextureView ? customDepthTextureView : customStencilTextureView; + + if (hasDepth) { + mDepthStencilAttachmentDescriptor.depthLoadOp = + WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH); + mDepthStencilAttachmentDescriptor.depthStoreOp = + WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH); + mDepthStencilAttachmentDescriptor.depthClearValue = + static_cast(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 (hasStencil) { + 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; } - }// namespace filament::backend diff --git a/filament/backend/src/webgpu/WebGPUHandles.h b/filament/backend/src/webgpu/WebGPUHandles.h index 07be0eef3e..f42ed4ec9f 100644 --- a/filament/backend/src/webgpu/WebGPUHandles.h +++ b/filament/backend/src/webgpu/WebGPUHandles.h @@ -179,7 +179,8 @@ public: size_t getBlockHeight() const { return mBlockHeight; } [[nodiscard]] const wgpu::Texture& getTexture() const { return mTexture; } - [[nodiscard]] const wgpu::TextureView& getTexView() const { return mTexView; } + [[nodiscard]] const wgpu::TextureView& getTextureView() const { return mTexureView; } + [[nodiscard]] wgpu::TextureFormat getFormat() const { return mFormat; } static wgpu::TextureFormat fToWGPUTextureFormat( filament::backend::TextureFormat const& fFormat); @@ -198,7 +199,7 @@ private: wgpu::TextureFormat mFormat = wgpu::TextureFormat::Undefined; wgpu::TextureAspect mAspect = wgpu::TextureAspect::Undefined; uint32_t mArrayLayerCount = 1; - wgpu::TextureView mTexView = nullptr; + wgpu::TextureView mTexureView = nullptr; wgpu::TextureUsage fToWGPUTextureUsage(filament::backend::TextureUsage const& fUsage); size_t mBlockWidth; size_t mBlockHeight; @@ -212,55 +213,60 @@ struct WGPURenderPrimitive : public HwRenderPrimitive { 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, + wgpu::TextureFormat customDepthFormat, + wgpu::TextureFormat customStencilFormat); - math::uint2 getAttachmentSize() 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: bool defaultRenderTarget = false; uint8_t samples = 1; - Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {}; - math::uint2 attachmentSize = {}; - std::vector colorAttachments{}; -}; + MRT mColorAttachments{}; + // TODO WebGPU only supports a DepthStencil attachment, should this be just mDepthStencilAttachment? + Attachment mDepthAttachment{}; + Attachment mStencilAttachment{}; + // Cached descriptors for the render pass + std::vector mColorAttachmentDescriptors; + wgpu::RenderPassDepthStencilAttachment mDepthStencilAttachmentDescriptor{}; + bool mHasDepthStencilAttachment = false; +}; }// namespace filament::backend #endif// TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H diff --git a/filament/backend/src/webgpu/WebGPUPipelineCreation.cpp b/filament/backend/src/webgpu/WebGPUPipelineCreation.cpp index e25f9b738d..1c0330b473 100644 --- a/filament/backend/src/webgpu/WebGPUPipelineCreation.cpp +++ b/filament/backend/src/webgpu/WebGPUPipelineCreation.cpp @@ -154,9 +154,9 @@ constexpr wgpu::BlendFactor toWebGPU(BlendFunction blendFunction) { wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device, WGPUProgram const& program, WGPUVertexBufferInfo const& vertexBufferInfo, wgpu::PipelineLayout const& layout, RasterState const& rasterState, - StencilState const& stencilState, PolygonOffset const& polygonOffset, - PrimitiveType primitiveType, wgpu::TextureFormat colorFormat, - wgpu::TextureFormat depthFormat) { + StencilState const& stencilState, PolygonOffset const& polygonOffset, PrimitiveType primitiveType, + std::vector const& colorFormats, + wgpu::TextureFormat depthFormat, uint8_t samplesCount) { assert_invariant(program.vertexShaderModule); const wgpu::DepthStencilState depthStencilState { .format = depthFormat, @@ -207,9 +207,9 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device, .unclippedDepth = !rasterState.depthClamp && device.HasFeature(wgpu::FeatureName::DepthClipControl) }, - .depthStencil = &depthStencilState, + .depthStencil = depthFormat != wgpu::TextureFormat::Undefined ? &depthStencilState: nullptr, .multisample = { - .count = 1, // TODO need to get this from the render target + .count = samplesCount, .mask = 0xFFFFFFFF, .alphaToCoverageEnabled = rasterState.alphaToCoverage }, @@ -234,12 +234,15 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device, fragmentState.entryPoint = "main"; fragmentState.constantCount = program.constants.size(), fragmentState.constants = program.constants.data(), - fragmentState.targetCount = 1; // TODO need to get this from the render target + fragmentState.targetCount = colorFormats.size(); fragmentState.targets = colorTargets.data(); assert_invariant(fragmentState.targetCount <= MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT); + // A fragment shader implies at least one color target if it outputs color. + // This should be guaranteed by the caller ensuring colorFormats is not empty. + assert_invariant(fragmentState.targetCount > 0); for (size_t targetIndex = 0; targetIndex < fragmentState.targetCount; targetIndex++) { auto& colorTarget = colorTargets[targetIndex]; - colorTarget.format = colorFormat; + colorTarget.format = colorFormats[targetIndex]; colorTarget.blend = rasterState.hasBlending() ? &blendState : nullptr; colorTarget.writeMask = rasterState.colorWrite ? wgpu::ColorWriteMask::All : wgpu::ColorWriteMask::None; diff --git a/filament/backend/src/webgpu/WebGPUPipelineCreation.h b/filament/backend/src/webgpu/WebGPUPipelineCreation.h index cdcc36a985..72bed38500 100644 --- a/filament/backend/src/webgpu/WebGPUPipelineCreation.h +++ b/filament/backend/src/webgpu/WebGPUPipelineCreation.h @@ -18,6 +18,7 @@ #define TNT_FILAMENT_BACKEND_WEBGPUPIPELINECREATION_H #include +#include namespace wgpu { class Device; @@ -38,8 +39,9 @@ class WGPUProgram; [[nodiscard]] wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const&, WGPUProgram const&, WGPUVertexBufferInfo const&, wgpu::PipelineLayout const&, - RasterState const&, StencilState const&, PolygonOffset const&, PrimitiveType, - wgpu::TextureFormat colorFormat, wgpu::TextureFormat depthFormat); + RasterState const&, StencilState const&, PolygonOffset const&, PrimitiveType primitiveType, + std::vector const& colorFormats, + wgpu::TextureFormat depthFormat, uint8_t samplesCount); }// namespace filament::backend