From a877f670d8701de6e3bc290df01680e260baef7a Mon Sep 17 00:00:00 2001 From: Jaswant Panchumarti <jaswant.panchumarti@kitware.com> Date: Wed, 4 Dec 2024 10:01:05 -0500 Subject: [PATCH] Use render bundle per vtkRenderer - this eliminates the link b/w a vtkActor with a render bundle. - that link made it awkward when dealing with actors that are nested inside other actors. Ex: vtkAxesActor. --- Rendering/WebGPU/vtkWebGPUActor.cxx | 89 ++++- Rendering/WebGPU/vtkWebGPUActor.h | 33 +- .../WebGPU/vtkWebGPUBatchedPolyDataMapper.cxx | 4 +- Rendering/WebGPU/vtkWebGPUCamera.cxx | 5 +- Rendering/WebGPU/vtkWebGPUClearDrawPass.cxx | 1 - Rendering/WebGPU/vtkWebGPUPolyDataMapper.cxx | 37 +-- Rendering/WebGPU/vtkWebGPURenderer.cxx | 309 +++--------------- Rendering/WebGPU/vtkWebGPURenderer.h | 42 +-- .../wgsl/LineMiterJoinVertexShader.wgsl | 2 +- .../wgsl/LineRoundJoinVertexShader.wgsl | 2 +- Rendering/WebGPU/wgsl/PointShader.wgsl | 2 +- Rendering/WebGPU/wgsl/SurfaceMeshShader.wgsl | 2 +- 12 files changed, 188 insertions(+), 340 deletions(-) diff --git a/Rendering/WebGPU/vtkWebGPUActor.cxx b/Rendering/WebGPU/vtkWebGPUActor.cxx index d83131ccc7d..372a767bdd7 100644 --- a/Rendering/WebGPU/vtkWebGPUActor.cxx +++ b/Rendering/WebGPU/vtkWebGPUActor.cxx @@ -3,6 +3,9 @@ #include "vtkWebGPUActor.h" +#include "Private/vtkWebGPUBindGroupInternals.h" +#include "Private/vtkWebGPUBindGroupLayoutInternals.h" + #include "vtkMapper.h" #include "vtkMatrix3x3.h" #include "vtkObjectFactory.h" @@ -12,6 +15,7 @@ #include "vtkTexture.h" #include "vtkTransform.h" #include "vtkWebGPUComputePointCloudMapper.h" +#include "vtkWebGPURenderWindow.h" #include "vtkWebGPURenderer.h" #include "vtkWindow.h" @@ -48,12 +52,14 @@ void vtkWebGPUActor::PrintSelf(ostream& os, vtkIndent indent) os << indent << "ModelTransformsBuildTimestamp: " << this->ModelTransformsBuildTimestamp << '\n'; os << indent << "ShadingOptionsBuildTimestamp: " << this->ShadingOptionsBuildTimestamp << '\n'; os << indent << "RenderOptionsBuildTimestamp: " << this->RenderOptionsBuildTimestamp << '\n'; - os << indent << "BundleInvalidated: " << this->BundleInvalidated << '\n'; } //------------------------------------------------------------------------------ void vtkWebGPUActor::ReleaseGraphicsResources(vtkWindow* window) { + this->ActorBindGroupLayout = nullptr; + this->ActorBindGroup = nullptr; + this->ActorBuffer = nullptr; this->MapperHasOpaqueGeometry = {}; this->MapperHasTranslucentPolygonalGeometry = {}; this->Superclass::ReleaseGraphicsResources(window); @@ -64,21 +70,46 @@ void vtkWebGPUActor::Render(vtkRenderer* renderer, vtkMapper* mapper) { if (auto* wgpuRenderer = vtkWebGPURenderer::SafeDownCast(renderer)) { + auto* wgpuRenderWindow = vtkWebGPURenderWindow::SafeDownCast(wgpuRenderer->GetRenderWindow()); + auto* wgpuConfiguration = wgpuRenderWindow->GetWGPUConfiguration(); switch (wgpuRenderer->GetRenderStage()) { case vtkWebGPURenderer::AwaitingPreparation: break; case vtkWebGPURenderer::UpdatingBuffers: + { + if (!(this->ActorBindGroup && this->ActorBindGroupLayout && this->ActorBuffer)) + { + this->AllocateResources(wgpuConfiguration); + } // reset this flag because the `mapper->Render()` call shall invalidate the bundle if it // determines that the render bundle needs to be recorded once again. - this->BundleInvalidated = false; mapper->Render(renderer, this); - this->CacheActorRenderOptions(); - this->CacheActorShadeOptions(); - this->CacheActorTransforms(); + bool updateBuffers = this->CacheActorRenderOptions(); + updateBuffers |= this->CacheActorShadeOptions(); + updateBuffers |= this->CacheActorTransforms(); + if (updateBuffers) + { + wgpuConfiguration->WriteBuffer(this->ActorBuffer, 0, this->GetCachedActorInformation(), + this->GetCacheSizeBytes(), "ActorBufferUpdate"); + } break; + } case vtkWebGPURenderer::RecordingCommands: - mapper->Render(renderer, this); + if (wgpuRenderer->GetUseRenderBundles() && this->SupportRenderBundles()) + { + if (wgpuRenderer->GetBundleInvalidated()) + { + wgpuRenderer->GetRenderBundleEncoder().SetBindGroup(1, this->ActorBindGroup); + mapper->Render(renderer, this); + } + // else, no need to record draw commands. + } + else + { + wgpuRenderer->GetRenderPassEncoder().SetBindGroup(1, this->ActorBindGroup); + mapper->Render(renderer, this); + } break; case vtkWebGPURenderer::Finished: default: @@ -207,7 +238,7 @@ bool vtkWebGPUActor::UpdateKeyMatrices() } //------------------------------------------------------------------------------ -void vtkWebGPUActor::CacheActorTransforms() +bool vtkWebGPUActor::CacheActorTransforms() { if (this->UpdateKeyMatrices()) { @@ -223,11 +254,13 @@ void vtkWebGPUActor::CacheActorTransforms() transform.Normal[i][j] = this->NormalMatrix->GetElement(i, j); } } + return true; } + return false; } //------------------------------------------------------------------------------ -void vtkWebGPUActor::CacheActorRenderOptions() +bool vtkWebGPUActor::CacheActorRenderOptions() { auto* displayProperty = this->GetProperty(); if (displayProperty->GetMTime() > this->RenderOptionsBuildTimestamp || @@ -246,11 +279,13 @@ void vtkWebGPUActor::CacheActorRenderOptions() (displayProperty->GetRenderLinesAsTubes() << 6) | (static_cast<int>(displayProperty->GetPoint2DShape()) << 7); this->RenderOptionsBuildTimestamp.Modified(); + return true; } + return false; } //------------------------------------------------------------------------------ -void vtkWebGPUActor::CacheActorShadeOptions() +bool vtkWebGPUActor::CacheActorShadeOptions() { auto* displayProperty = this->GetProperty(); if (displayProperty->GetMTime() > this->ShadingOptionsBuildTimestamp || @@ -271,6 +306,42 @@ void vtkWebGPUActor::CacheActorShadeOptions() so.VertexColor[i] = displayProperty->GetVertexColor()[i]; } this->ShadingOptionsBuildTimestamp.Modified(); + return true; } + return false; +} + +//------------------------------------------------------------------------------ +void vtkWebGPUActor::AllocateResources(vtkWebGPUConfiguration* wgpuConfiguration) +{ + const auto& device = wgpuConfiguration->GetDevice(); + + const auto actorDescription = this->GetObjectDescription(); + const auto bufferLabel = "buffer@" + actorDescription; + const auto bufferSize = vtkWebGPUConfiguration::Align(vtkWebGPUActor::GetCacheSizeBytes(), 32); + this->ActorBuffer = wgpuConfiguration->CreateBuffer(bufferSize, + wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst, false, bufferLabel.c_str()); + + this->ActorBindGroupLayout = vtkWebGPUBindGroupLayoutInternals::MakeBindGroupLayout(device, + { + // clang-format off + // ActorBlocks + { 0, wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::ReadOnlyStorage }, + // clang-format on + }, + "bgl@" + actorDescription); + this->ActorBindGroup = + vtkWebGPUBindGroupInternals::MakeBindGroup(device, this->ActorBindGroupLayout, + { + // clang-format off + { 0, this->ActorBuffer, 0, bufferSize }, + // clang-format on + }, + "bg@" + actorDescription); + // Reset timestamps because the previous buffer is now gone and contents of the buffer will need + // to be re-uploaded. + this->ModelTransformsBuildTimestamp = vtkTimeStamp(); + this->ShadingOptionsBuildTimestamp = vtkTimeStamp(); + this->RenderOptionsBuildTimestamp = vtkTimeStamp(); } VTK_ABI_NAMESPACE_END diff --git a/Rendering/WebGPU/vtkWebGPUActor.h b/Rendering/WebGPU/vtkWebGPUActor.h index 57891f6371b..002ace5cf24 100644 --- a/Rendering/WebGPU/vtkWebGPUActor.h +++ b/Rendering/WebGPU/vtkWebGPUActor.h @@ -10,6 +10,7 @@ VTK_ABI_NAMESPACE_BEGIN class vtkMatrix3x3; +class vtkWebGPUConfiguration; class vtkWebGPURenderPipelineCache; class VTKRENDERINGWEBGPU_EXPORT vtkWebGPUActor : public vtkActor @@ -19,6 +20,8 @@ public: vtkTypeMacro(vtkWebGPUActor, vtkActor); void PrintSelf(ostream& os, vtkIndent indent) override; + void ReleaseGraphicsResources(vtkWindow* window) override; + inline const void* GetCachedActorInformation() { return &(this->CachedActorInfo); } static std::size_t GetCacheSizeBytes() { return sizeof(ActorBlock); } @@ -35,6 +38,11 @@ public: */ bool SupportRenderBundles(); + inline void PopulateBindgroupLayouts(std::vector<wgpu::BindGroupLayout>& layouts) + { + layouts.emplace_back(this->ActorBindGroupLayout); + } + virtual bool UpdateKeyMatrices(); ///@{ @@ -55,26 +63,15 @@ public: vtkTypeBool HasTranslucentPolygonalGeometry() override; ///@} - /** - * Forces the renderer to re-record draw commands into a render bundle associated with this actor. - * - * @note This does not use vtkSetMacro because the actor MTime should not be affected when a - * render bundle is invalidated. - */ - inline void SetBundleInvalidated(bool value) { this->BundleInvalidated = value; } - - /** - * Get whether the render bundle associated with this actor must be reset by the renderer. - */ - vtkGetMacro(BundleInvalidated, bool); - protected: vtkWebGPUActor(); ~vtkWebGPUActor() override; - void CacheActorTransforms(); - void CacheActorRenderOptions(); - void CacheActorShadeOptions(); + bool CacheActorTransforms(); + bool CacheActorRenderOptions(); + bool CacheActorShadeOptions(); + + void AllocateResources(vtkWebGPUConfiguration* renderer); struct ActorBlock { @@ -134,7 +131,9 @@ protected: vtkTimeStamp ShadingOptionsBuildTimestamp; vtkTimeStamp RenderOptionsBuildTimestamp; - bool BundleInvalidated = false; + wgpu::BindGroupLayout ActorBindGroupLayout; + wgpu::BindGroup ActorBindGroup; + wgpu::Buffer ActorBuffer; class MapperBooleanCache { diff --git a/Rendering/WebGPU/vtkWebGPUBatchedPolyDataMapper.cxx b/Rendering/WebGPU/vtkWebGPUBatchedPolyDataMapper.cxx index dbaf78d08e7..883e0e164f2 100644 --- a/Rendering/WebGPU/vtkWebGPUBatchedPolyDataMapper.cxx +++ b/Rendering/WebGPU/vtkWebGPUBatchedPolyDataMapper.cxx @@ -138,14 +138,14 @@ void vtkWebGPUBatchedPolyDataMapper::RenderPiece(vtkRenderer* renderer, vtkActor } auto* wgpuRenderWindow = vtkWebGPURenderWindow::SafeDownCast(renderer->GetRenderWindow()); - auto* wgpuActor = vtkWebGPUActor::SafeDownCast(actor); + auto* wgpuRenderer = vtkWebGPURenderer::SafeDownCast(renderer); auto* wgpuConfiguration = wgpuRenderWindow->GetWGPUConfiguration(); const auto& device = wgpuRenderWindow->GetDevice(); auto& batchElement = *(this->VTKPolyDataToBatchElement.begin()->second); if (this->LastBlockVisibility != batchElement.Visibility) { - wgpuActor->SetBundleInvalidated(true); + wgpuRenderer->SetBundleInvalidated(true); } this->LastBlockVisibility = batchElement.Visibility; diff --git a/Rendering/WebGPU/vtkWebGPUCamera.cxx b/Rendering/WebGPU/vtkWebGPUCamera.cxx index aa36d2e8b92..7314901cc16 100644 --- a/Rendering/WebGPU/vtkWebGPUCamera.cxx +++ b/Rendering/WebGPU/vtkWebGPUCamera.cxx @@ -119,13 +119,12 @@ void vtkWebGPUCamera::UpdateViewport(vtkRenderer* renderer) rpassEncoder.SetScissorRect(static_cast<uint32_t>(this->ScissorRect.GetLeft()), static_cast<uint32_t>(this->ScissorRect.GetBottom()), static_cast<uint32_t>(this->ScissorRect.GetWidth()), - static_cast<uint32_t>(this->ScissorRect.GetWidth())); + static_cast<uint32_t>(this->ScissorRect.GetHeight())); this->UseScissor = false; } else { - rpassEncoder.SetScissorRect( - 0u, 0u, static_cast<uint32_t>(width), static_cast<uint32_t>(height)); + rpassEncoder.SetScissorRect(0, 0, static_cast<uint32_t>(width), static_cast<uint32_t>(height)); } } diff --git a/Rendering/WebGPU/vtkWebGPUClearDrawPass.cxx b/Rendering/WebGPU/vtkWebGPUClearDrawPass.cxx index 33c33ac07cc..df3e159adcc 100644 --- a/Rendering/WebGPU/vtkWebGPUClearDrawPass.cxx +++ b/Rendering/WebGPU/vtkWebGPUClearDrawPass.cxx @@ -7,7 +7,6 @@ #include "vtkRenderState.h" #include "vtkRenderer.h" #include "vtkWebGPURenderWindow.h" -#include <chrono> VTK_ABI_NAMESPACE_BEGIN diff --git a/Rendering/WebGPU/vtkWebGPUPolyDataMapper.cxx b/Rendering/WebGPU/vtkWebGPUPolyDataMapper.cxx index ffc9151f01d..cfdc04fa74b 100644 --- a/Rendering/WebGPU/vtkWebGPUPolyDataMapper.cxx +++ b/Rendering/WebGPU/vtkWebGPUPolyDataMapper.cxx @@ -282,7 +282,6 @@ void vtkWebGPUPolyDataMapper::RenderPiece(vtkRenderer* renderer, vtkActor* actor const auto device = wgpuRenderWindow->GetDevice(); auto* wgpuConfiguration = wgpuRenderWindow->GetWGPUConfiguration(); - auto* wgpuActor = reinterpret_cast<vtkWebGPUActor*>(actor); auto* wgpuRenderer = vtkWebGPURenderer::SafeDownCast(renderer); auto* displayProperty = actor->GetProperty(); @@ -307,13 +306,13 @@ void vtkWebGPUPolyDataMapper::RenderPiece(vtkRenderer* renderer, vtkActor* actor if (this->GetNeedToRebuildGraphicsPipelines(actor)) { // render bundle must reference new bind groups and/or pipelines - wgpuActor->SetBundleInvalidated(true); + wgpuRenderer->SetBundleInvalidated(true); this->SetupGraphicsPipelines(device, renderer, actor); } // invalidate render bundle when any of the cached properties of an actor have changed. if (this->CacheActorProperties(actor)) { - wgpuActor->SetBundleInvalidated(true); + wgpuRenderer->SetBundleInvalidated(true); } break; } @@ -1257,7 +1256,7 @@ void vtkWebGPUPolyDataMapper::UpdateMeshGeometryBuffers(vtkWebGPURenderWindow* w vtkCellData* cellData = this->CurrentInput->GetCellData(); vtkDataArray* cellColors = nullptr; - vtkNew<vtkUnsignedCharArray> cellColorsFromFieldData; + vtkSmartPointer<vtkUnsignedCharArray> cellColorsFromFieldData; if (this->HasCellAttributes[CELL_COLORS]) { // are we using a single color value replicated over all cells? @@ -1265,6 +1264,7 @@ void vtkWebGPUPolyDataMapper::UpdateMeshGeometryBuffers(vtkWebGPURenderWindow* w { const vtkIdType numCells = this->CurrentInput->GetNumberOfCells(); const int numComponents = this->Colors->GetNumberOfComponents(); + cellColorsFromFieldData = vtk::TakeSmartPointer(vtkUnsignedCharArray::New()); cellColorsFromFieldData->SetNumberOfComponents(numComponents); cellColorsFromFieldData->SetNumberOfTuples(numCells); for (int i = 0; i < numComponents; ++i) @@ -1377,19 +1377,23 @@ void vtkWebGPUPolyDataMapper::UpdateMeshGeometryBuffers(vtkWebGPURenderWindow* w } } - const std::string meshAttrDescriptorLabel = - "MeshAttributeDescriptor-" + this->CurrentInput->GetObjectDescription(); - if (this->AttributeDescriptorBuffer == nullptr) - { - this->AttributeDescriptorBuffer = wgpuConfiguration->CreateBuffer(sizeof(meshAttrDescriptor), - wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst, - /*mappedAtCreation=*/false, meshAttrDescriptorLabel.c_str()); - } // handle partial updates if (updatePointDescriptor || updateCellArrayDescriptor) { + const std::string meshAttrDescriptorLabel = + "MeshAttributeDescriptor-" + this->CurrentInput->GetObjectDescription(); + if (this->AttributeDescriptorBuffer == nullptr) + { + this->AttributeDescriptorBuffer = wgpuConfiguration->CreateBuffer(sizeof(meshAttrDescriptor), + wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst, + /*mappedAtCreation=*/false, meshAttrDescriptorLabel.c_str()); + } wgpuConfiguration->WriteBuffer(this->AttributeDescriptorBuffer, 0, &meshAttrDescriptor, sizeof(meshAttrDescriptor), meshAttrDescriptorLabel.c_str()); + // Create bind group for the point/cell attribute buffers. + this->MeshAttributeBindGroup = + this->CreateMeshAttributeBindGroup(wgpuConfiguration->GetDevice(), "MeshAttributeBindGroup"); + this->RebuildGraphicsPipelines = true; } vtkDebugMacro(<< ((updatePointDescriptor || updateCellArrayDescriptor) ? "rebuilt" : "") @@ -1399,13 +1403,6 @@ void vtkWebGPUPolyDataMapper::UpdateMeshGeometryBuffers(vtkWebGPURenderWindow* w << ((updatePointDescriptor || updateCellArrayDescriptor) ? " buffers" : "reuse point and cell buffers")); - if (updatePointDescriptor || updateCellArrayDescriptor) - { - // Create bind group for the point/cell attribute buffers. - this->MeshAttributeBindGroup = - this->CreateMeshAttributeBindGroup(wgpuConfiguration->GetDevice(), "MeshAttributeBindGroup"); - this->RebuildGraphicsPipelines = true; - } } //------------------------------------------------------------------------------ @@ -1833,6 +1830,7 @@ void vtkWebGPUPolyDataMapper::DispatchCellToPrimitiveComputePipeline( void vtkWebGPUPolyDataMapper::SetupGraphicsPipelines( const wgpu::Device& device, vtkRenderer* renderer, vtkActor* actor) { + auto* wgpuActor = vtkWebGPUActor::SafeDownCast(actor); auto* wgpuRenderWindow = vtkWebGPURenderWindow::SafeDownCast(renderer->GetRenderWindow()); auto* wgpuRenderer = vtkWebGPURenderer::SafeDownCast(renderer); auto* wgpuPipelineCache = wgpuRenderWindow->GetWGPUPipelineCache(); @@ -1862,6 +1860,7 @@ void vtkWebGPUPolyDataMapper::SetupGraphicsPipelines( std::vector<wgpu::BindGroupLayout> bgls; wgpuRenderer->PopulateBindgroupLayouts(bgls); + wgpuActor->PopulateBindgroupLayouts(bgls); bgls.emplace_back( this->CreateMeshAttributeBindGroupLayout(device, "MeshAttributeBindGroupLayout")); bgls.emplace_back(this->CreateTopologyBindGroupLayout(device, "TopologyBindGroupLayout")); diff --git a/Rendering/WebGPU/vtkWebGPURenderer.cxx b/Rendering/WebGPU/vtkWebGPURenderer.cxx index 3221de34516..86409c94176 100644 --- a/Rendering/WebGPU/vtkWebGPURenderer.cxx +++ b/Rendering/WebGPU/vtkWebGPURenderer.cxx @@ -3,7 +3,6 @@ #include "vtkWebGPURenderer.h" #include "Private/vtkWebGPUBindGroupInternals.h" #include "Private/vtkWebGPUBindGroupLayoutInternals.h" -#include "Private/vtkWebGPUBufferInternals.h" #include "Private/vtkWebGPUComputePassInternals.h" #include "vtkAbstractMapper.h" #include "vtkFrameBufferObjectBase.h" @@ -92,32 +91,6 @@ std::size_t vtkWebGPURenderer::WriteLightsBuffer(std::size_t offset /*=0*/) return wroteBytes; } -//------------------------------------------------------------------------------ -std::size_t vtkWebGPURenderer::WriteActorBlocksBuffer(std::size_t offset /*=0*/) -{ - std::size_t wroteBytes = 0; - auto* wgpuRenderWindow = vtkWebGPURenderWindow::SafeDownCast(this->GetRenderWindow()); - auto* wgpuConfiguration = wgpuRenderWindow->GetWGPUConfiguration(); - - const auto size = vtkWebGPUConfiguration::Align(vtkWebGPUActor::GetCacheSizeBytes(), 256); - std::vector<uint8_t> stage; - stage.resize(this->Props->GetNumberOfItems() * size); - vtkProp* aProp = nullptr; - vtkCollectionSimpleIterator piter; - for (this->Props->InitTraversal(piter); (aProp = this->Props->GetNextProp(piter));) - { - vtkWebGPUActor* wgpuActor = reinterpret_cast<vtkWebGPUActor*>(aProp); - assert(wgpuActor != nullptr); - - const auto data = wgpuActor->GetCachedActorInformation(); - std::memcpy(&stage[wroteBytes], data, size); - wroteBytes += size; - } - wgpuConfiguration->WriteBuffer( - this->ActorBlocksBuffer, offset, stage.data(), wroteBytes, "ActorInformation"); - return wroteBytes; -} - //------------------------------------------------------------------------------ void vtkWebGPURenderer::CreateBuffers() { @@ -128,15 +101,9 @@ void vtkWebGPURenderer::CreateBuffers() + this->LightIDs.size() * vtkWebGPULight::GetCacheSizeBytes(); const auto lightSizePadded = vtkWebGPUConfiguration::Align(lightSize, 32); - // use padded for actor because dynamic offsets are used. - const auto actorBlkSize = vtkMath::Max<std::size_t>(this->Props->GetNumberOfItems() * - vtkWebGPUConfiguration::Align(vtkWebGPUActor::GetCacheSizeBytes(), 256), - 256); - auto* wgpuRenderWindow = vtkWebGPURenderWindow::SafeDownCast(this->GetRenderWindow()); auto* wgpuConfiguration = wgpuRenderWindow->GetWGPUConfiguration(); bool createSceneBindGroup = false; - bool createActorBindGroup = false; if (this->SceneTransformBuffer == nullptr) { @@ -154,46 +121,10 @@ void vtkWebGPURenderer::CreateBuffers() createSceneBindGroup = true; } - if (actorBlkSize != this->LastActorBufferSize) - { - if (this->ActorBlocksBuffer != nullptr) - { - this->ActorBlocksBuffer.Destroy(); - this->ActorBlocksBuffer = nullptr; - } - } - if (this->ActorBlocksBuffer == nullptr) - { - const std::string label = "ActorInformation-" + this->GetObjectDescription(); - this->ActorBlocksBuffer = wgpuConfiguration->CreateBuffer( - actorBlkSize, wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst, false, label.c_str()); - this->LastActorBufferSize = actorBlkSize; - createActorBindGroup = true; - } - if (createSceneBindGroup) { this->SetupSceneBindGroup(); } - - if (createActorBindGroup) - { - // delete outdated info. - this->PropWGPUItems.clear(); - this->SetupActorBindGroup(); - // build new cache for bundles and dynamic offsets. - vtkProp* aProp = nullptr; - vtkCollectionSimpleIterator piter; - const auto size = vtkWebGPUConfiguration::Align(vtkWebGPUActor::GetCacheSizeBytes(), 256); - int i = 0; - for (this->Props->InitTraversal(piter); (aProp = this->Props->GetNextProp(piter)); i++) - { - vtkWGPUPropItem item; - item.Bundle = nullptr; - item.DynamicOffset = i * size; - this->PropWGPUItems.emplace(aProp, item); - } - } } //------------------------------------------------------------------------------ @@ -228,12 +159,14 @@ void vtkWebGPURenderer::UpdateBuffers() this->UpdateCamera(); // brings the camera's transform matrices up-to-date. this->UpdateLightGeometry(); this->UpdateLights(); + + // if any mapper's commands need rebundling, that mapper will set this flag. + this->BundleInvalidated = false; this->UpdateGeometry(); // mappers prepare geometry SSBO and pipeline layout. this->CreateBuffers(); this->WriteSceneTransformsBuffer(); this->WriteLightsBuffer(); - this->WriteActorBlocksBuffer(); } //------------------------------------------------------------------------------ @@ -349,14 +282,7 @@ int vtkWebGPURenderer::UpdateOpaquePolygonalGeometry() { for (int i = 0; i < this->PropArrayCount; i++) { - auto wgpuActor = reinterpret_cast<vtkWebGPUActor*>(this->PropArray[i]); - wgpuActor->RenderOpaqueGeometry(this); - if (wgpuActor->GetBundleInvalidated()) - { - // mapper's buffers and bind points have changed. bundle is outdated. - auto& wgpuPropItem = this->PropWGPUItems[this->PropArray[i]]; - wgpuPropItem.Bundle = nullptr; - } + this->PropArray[i]->RenderOpaqueGeometry(this); } result += this->PropArrayCount; } @@ -365,69 +291,12 @@ int vtkWebGPURenderer::UpdateOpaquePolygonalGeometry() { for (int i = 0; i < this->PropArrayCount; i++) { - if (auto* wgpuActor = vtkWebGPUActor::SafeDownCast(this->PropArray[i])) - { - int rendered = 0; - auto& wgpuPropItem = this->PropWGPUItems[this->PropArray[i]]; - if (wgpuActor->SupportRenderBundles() && this->UseRenderBundles) - { - this->BundleCacheStats.TotalRequests++; - if (wgpuPropItem.Bundle == nullptr) - { - const std::string label = this->GetObjectDescription(); - auto wgpuRenderWindow = vtkWebGPURenderWindow::SafeDownCast(this->GetRenderWindow()); - const auto colorFormat = wgpuRenderWindow->GetPreferredSurfaceTextureFormat(); - const int sampleCount = - wgpuRenderWindow->GetMultiSamples() ? wgpuRenderWindow->GetMultiSamples() : 1; - wgpu::RenderBundleEncoderDescriptor bundleEncDesc; - bundleEncDesc.colorFormatCount = 1; - bundleEncDesc.colorFormats = &colorFormat; - bundleEncDesc.depthStencilFormat = wgpuRenderWindow->GetDepthStencilFormat(); - bundleEncDesc.sampleCount = sampleCount; - bundleEncDesc.depthReadOnly = false; - bundleEncDesc.stencilReadOnly = false; - bundleEncDesc.label = label.c_str(); - bundleEncDesc.nextInChain = nullptr; - this->WGPUBundleEncoder = wgpuRenderWindow->NewRenderBundleEncoder(bundleEncDesc); - this->WGPUBundleEncoder.SetBindGroup(0, this->SceneBindGroup); - this->WGPUBundleEncoder.SetBindGroup( - 1, this->ActorBindGroup, 1, &wgpuPropItem.DynamicOffset); - rendered = wgpuActor->RenderOpaqueGeometry(this); - auto bundle = this->WGPUBundleEncoder.Finish(); - this->WGPUBundleEncoder = nullptr; - if (rendered > 0) - { - wgpuPropItem.Bundle = bundle; - this->Bundles.emplace_back(wgpuPropItem.Bundle); - } - this->BundleCacheStats.Misses++; - } - else - { - // bundle gets reused for this prop. - rendered = 1; - this->Bundles.emplace_back(wgpuPropItem.Bundle); - this->BundleCacheStats.Hits++; - } - } - else - { - this->WGPURenderEncoder.SetBindGroup( - 1, this->ActorBindGroup, 1, &wgpuPropItem.DynamicOffset); - rendered = wgpuActor->RenderOpaqueGeometry(this); - } - if (rendered > 0) - { - result += rendered; - - this->NumberOfPropsRendered += rendered; - this->PropsRendered.insert(wgpuActor); - } - } - else + const int rendered = this->PropArray[i]->RenderOpaqueGeometry(this); + if (rendered > 0) { - vtkWarningMacro(<< "Prop " << this->PropArray[i]->GetObjectDescription() - << " is unrecognized in the vtkWebGPURenderer."); + result += rendered; + this->NumberOfPropsRendered += rendered; + this->PropsRendered.insert(this->PropArray[i]); } } } @@ -449,14 +318,7 @@ int vtkWebGPURenderer::UpdateTranslucentPolygonalGeometry() { for (int i = 0; i < this->PropArrayCount; i++) { - auto wgpuActor = reinterpret_cast<vtkWebGPUActor*>(this->PropArray[i]); - wgpuActor->RenderTranslucentPolygonalGeometry(this); - if (wgpuActor->GetBundleInvalidated()) - { - // mapper's buffers and bind points have changed. bundle is outdated. - auto& wgpuPropItem = this->PropWGPUItems[this->PropArray[i]]; - wgpuPropItem.Bundle = nullptr; - } + this->PropArray[i]->RenderTranslucentPolygonalGeometry(this); } result += this->PropArrayCount; } @@ -465,69 +327,12 @@ int vtkWebGPURenderer::UpdateTranslucentPolygonalGeometry() { for (int i = 0; i < this->PropArrayCount; i++) { - if (auto* wgpuActor = vtkWebGPUActor::SafeDownCast(this->PropArray[i])) + const int rendered = this->PropArray[i]->RenderTranslucentPolygonalGeometry(this); + if (rendered > 0) { - int rendered = 0; - auto& wgpuPropItem = this->PropWGPUItems[this->PropArray[i]]; - if (wgpuActor->SupportRenderBundles() && this->UseRenderBundles) - { - this->BundleCacheStats.TotalRequests++; - if (wgpuPropItem.Bundle == nullptr) - { - const std::string label = this->GetObjectDescription(); - auto wgpuRenderWindow = vtkWebGPURenderWindow::SafeDownCast(this->GetRenderWindow()); - const auto colorFormat = wgpuRenderWindow->GetPreferredSurfaceTextureFormat(); - const int sampleCount = - wgpuRenderWindow->GetMultiSamples() ? wgpuRenderWindow->GetMultiSamples() : 1; - wgpu::RenderBundleEncoderDescriptor bundleEncDesc; - bundleEncDesc.colorFormatCount = 1; - bundleEncDesc.colorFormats = &colorFormat; - bundleEncDesc.depthStencilFormat = wgpuRenderWindow->GetDepthStencilFormat(); - bundleEncDesc.sampleCount = sampleCount; - bundleEncDesc.depthReadOnly = false; - bundleEncDesc.stencilReadOnly = false; - bundleEncDesc.label = label.c_str(); - bundleEncDesc.nextInChain = nullptr; - this->WGPUBundleEncoder = wgpuRenderWindow->NewRenderBundleEncoder(bundleEncDesc); - this->WGPUBundleEncoder.SetBindGroup(0, this->SceneBindGroup); - this->WGPUBundleEncoder.SetBindGroup( - 1, this->ActorBindGroup, 1, &wgpuPropItem.DynamicOffset); - rendered = wgpuActor->RenderTranslucentPolygonalGeometry(this); - auto bundle = this->WGPUBundleEncoder.Finish(); - this->WGPUBundleEncoder = nullptr; - if (rendered > 0) - { - wgpuPropItem.Bundle = bundle; - this->Bundles.emplace_back(wgpuPropItem.Bundle); - } - this->BundleCacheStats.Misses++; - } - else - { - // bundle gets reused for this prop. - rendered = 1; - this->Bundles.emplace_back(wgpuPropItem.Bundle); - this->BundleCacheStats.Hits++; - } - } - else - { - this->WGPURenderEncoder.SetBindGroup( - 1, this->ActorBindGroup, 1, &wgpuPropItem.DynamicOffset); - rendered = wgpuActor->RenderTranslucentPolygonalGeometry(this); - } - if (rendered > 0) - { - result += rendered; - - this->NumberOfPropsRendered += rendered; - this->PropsRendered.insert(wgpuActor); - } - } - else - { - vtkWarningMacro(<< "Prop " << this->PropArray[i]->GetObjectDescription() - << " is unrecognized in the vtkWebGPURenderer."); + result += rendered; + this->NumberOfPropsRendered += rendered; + this->PropsRendered.insert(this->PropArray[i]); } } } @@ -803,17 +608,14 @@ void vtkWebGPURenderer::SetEnvironmentTexture(vtkTexture*, bool vtkNotUsed(isSRG void vtkWebGPURenderer::ReleaseGraphicsResources(vtkWindow* w) { this->Superclass::ReleaseGraphicsResources(w); - this->Bundles.clear(); - this->PropWGPUItems.clear(); + this->Bundle = nullptr; + this->WGPUBundleEncoder = nullptr; this->WGPURenderEncoder = nullptr; this->SceneTransformBuffer = nullptr; this->SceneLightsBuffer = nullptr; - this->ActorBlocksBuffer = nullptr; this->LastActorBufferSize = 0; this->SceneBindGroup = nullptr; this->SceneBindGroupLayout = nullptr; - this->ActorBindGroup = nullptr; - this->ActorBindGroupLayout = nullptr; } //------------------------------------------------------------------------------ @@ -893,11 +695,32 @@ void vtkWebGPURenderer::BeginRecording() this->WGPURenderEncoder.PushDebugGroup("Renderer start encoding"); #endif this->WGPURenderEncoder.SetBindGroup(0, this->SceneBindGroup); - - this->Bundles.clear(); - this->BundleCacheStats.TotalRequests = 0; - this->BundleCacheStats.Hits = 0; - this->BundleCacheStats.Misses = 0; + if (this->BundleInvalidated) + { + // destroy previous bundle. + this->Bundle = nullptr; + // create a new bundle encoder. + const std::string label = this->GetObjectDescription(); + auto wgpuRenderWindow = vtkWebGPURenderWindow::SafeDownCast(this->GetRenderWindow()); + const auto colorFormat = wgpuRenderWindow->GetPreferredSurfaceTextureFormat(); + const int sampleCount = + wgpuRenderWindow->GetMultiSamples() ? wgpuRenderWindow->GetMultiSamples() : 1; + wgpu::RenderBundleEncoderDescriptor bundleEncDesc; + bundleEncDesc.colorFormatCount = 1; + bundleEncDesc.colorFormats = &colorFormat; + bundleEncDesc.depthStencilFormat = wgpuRenderWindow->GetDepthStencilFormat(); + bundleEncDesc.sampleCount = sampleCount; + bundleEncDesc.depthReadOnly = false; + bundleEncDesc.stencilReadOnly = false; + bundleEncDesc.label = label.c_str(); + bundleEncDesc.nextInChain = nullptr; + this->WGPUBundleEncoder = wgpuRenderWindow->NewRenderBundleEncoder(bundleEncDesc); + this->WGPUBundleEncoder.SetBindGroup(0, this->SceneBindGroup); + } + else + { + this->WGPUBundleEncoder = nullptr; + } } //------------------------------------------------------------------------------ @@ -919,19 +742,6 @@ void vtkWebGPURenderer::SetupBindGroupLayouts() this->SceneBindGroupLayout.SetLabel("SceneBindGroupLayout"); } - - if (this->ActorBindGroupLayout.Get() == nullptr) - { - this->ActorBindGroupLayout = vtkWebGPUBindGroupLayoutInternals::MakeBindGroupLayout(device, - { - // clang-format off - // ActorBlocks - { 0, wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Uniform, /*hasDynamicOffsets=*/true }, - // clang-format on - }); - - this->ActorBindGroupLayout.SetLabel("ActorBindGroupLayout"); - } } //------------------------------------------------------------------------------ @@ -951,21 +761,6 @@ void vtkWebGPURenderer::SetupSceneBindGroup() this->SceneBindGroup.SetLabel("SceneBindGroup"); } -//------------------------------------------------------------------------------ -void vtkWebGPURenderer::SetupActorBindGroup() -{ - auto wgpuRenderWindow = vtkWebGPURenderWindow::SafeDownCast(this->GetRenderWindow()); - wgpu::Device device = wgpuRenderWindow->GetDevice(); - this->ActorBindGroup = - vtkWebGPUBindGroupInternals::MakeBindGroup(device, this->ActorBindGroupLayout, - { - // clang-format off - { 0, this->ActorBlocksBuffer, 0, vtkWebGPUConfiguration::Align(vtkWebGPUActor::GetCacheSizeBytes(), 256) }, - // clang-format on - }); - this->ActorBindGroup.SetLabel("ActorBindGroup"); -} - //------------------------------------------------------------------------------ void vtkWebGPURenderer::EndRecording() { @@ -973,19 +768,13 @@ void vtkWebGPURenderer::EndRecording() this->RenderStage = RenderStageEnum::Finished; if (this->UseRenderBundles) { - if (!this->Bundles.empty() && this->BundleCacheStats.TotalRequests > 0) + if (this->WGPUBundleEncoder) + { + this->Bundle = this->WGPUBundleEncoder.Finish(); + } + if (this->Bundle != nullptr) { - vtkDebugMacro(<< "Bundle cache summary:\n" - << "Total requests: " << this->BundleCacheStats.TotalRequests << "\n" - << "Hit ratio: " - << (this->BundleCacheStats.Hits / this->BundleCacheStats.TotalRequests) * 100 - << "%\n" - << "Miss ratio: " - << (this->BundleCacheStats.Misses / this->BundleCacheStats.TotalRequests) * 100 - << "%\n" - << "Hit: " << this->BundleCacheStats.Hits << "\n" - << "Miss: " << this->BundleCacheStats.Misses << "\n"); - this->WGPURenderEncoder.ExecuteBundles(this->Bundles.size(), this->Bundles.data()); + this->WGPURenderEncoder.ExecuteBundles(1, &this->Bundle); } } #ifndef NDEBUG diff --git a/Rendering/WebGPU/vtkWebGPURenderer.h b/Rendering/WebGPU/vtkWebGPURenderer.h index 69b3b90bb33..c7333743f84 100644 --- a/Rendering/WebGPU/vtkWebGPURenderer.h +++ b/Rendering/WebGPU/vtkWebGPURenderer.h @@ -110,13 +110,11 @@ public: inline wgpu::RenderPassEncoder GetRenderPassEncoder() { return this->WGPURenderEncoder; } inline wgpu::RenderBundleEncoder GetRenderBundleEncoder() { return this->WGPUBundleEncoder; } - inline wgpu::BindGroup GetActorBindGroup() { return this->ActorBindGroup; } inline wgpu::BindGroup GetSceneBindGroup() { return this->SceneBindGroup; } inline void PopulateBindgroupLayouts(std::vector<wgpu::BindGroupLayout>& layouts) { layouts.emplace_back(this->SceneBindGroupLayout); - layouts.emplace_back(this->ActorBindGroupLayout); } /// @{ @@ -165,6 +163,19 @@ public: */ vtkGetEnumMacro(RenderStage, RenderStageEnum); + /** + * Forces the renderer to re-record draw commands into a render bundle. + * + * @note This does not use vtkSetMacro because the actor MTime should not be affected when a + * render bundle is invalidated. + */ + inline void SetBundleInvalidated(bool value) { this->BundleInvalidated = value; } + + /** + * Get whether the render bundle associated with this actor must be reset by the renderer. + */ + vtkGetMacro(BundleInvalidated, bool); + protected: vtkWebGPURenderer(); ~vtkWebGPURenderer() override; @@ -193,35 +204,23 @@ protected: std::size_t WriteLightsBuffer(std::size_t offset = 0); std::size_t WriteSceneTransformsBuffer(std::size_t offset = 0); - std::size_t WriteActorBlocksBuffer(std::size_t offset = 0); wgpu::RenderPassEncoder WGPURenderEncoder; wgpu::RenderBundleEncoder WGPUBundleEncoder; wgpu::Buffer SceneTransformBuffer; wgpu::Buffer SceneLightsBuffer; - wgpu::Buffer ActorBlocksBuffer; std::size_t LastActorBufferSize = 0; wgpu::BindGroup SceneBindGroup; wgpu::BindGroupLayout SceneBindGroupLayout; - wgpu::BindGroup ActorBindGroup; - wgpu::BindGroupLayout ActorBindGroupLayout; - #ifdef __EMSCRIPTEN__ bool UseRenderBundles = true; #else - bool UseRenderBundles = false; + bool UseRenderBundles = true; #endif - // one bundle per actor. bundle gets reused every frame. - // these bundles can be built in parallel with vtkSMPTools. holding off because not - // sure how to get emscripten to thread. - std::vector<wgpu::RenderBundle> Bundles; - struct vtkWGPUPropItem - { - wgpu::RenderBundle Bundle = nullptr; - vtkTypeUInt32 DynamicOffset = 0; - }; - std::unordered_map<vtkProp*, vtkWGPUPropItem> PropWGPUItems; + bool BundleInvalidated = false; + // the commands in bundle get reused every frame. + wgpu::RenderBundle Bundle; int LightingComplexity = 0; std::size_t NumberOfLightsUsed = 0; @@ -230,13 +229,6 @@ protected: vtkMTimeType LightingUpdateTime; vtkTimeStamp LightingUploadTimestamp; - struct - { - uint32_t Hits = 0; - uint32_t Misses = 0; - uint32_t TotalRequests = 0; - } BundleCacheStats; - /** * Optional user transform for lights */ diff --git a/Rendering/WebGPU/wgsl/LineMiterJoinVertexShader.wgsl b/Rendering/WebGPU/wgsl/LineMiterJoinVertexShader.wgsl index e25057be618..f29e73cbe1a 100644 --- a/Rendering/WebGPU/wgsl/LineMiterJoinVertexShader.wgsl +++ b/Rendering/WebGPU/wgsl/LineMiterJoinVertexShader.wgsl @@ -85,7 +85,7 @@ struct Topology { ///------------------------------------------------------------------- // Everything shader needs from the vtkActor and it's vtkProperty ///------------------------------------------------------------------- -@group(1) @binding(0) var<uniform> actor: ActorBlock; +@group(1) @binding(0) var<storage, read> actor: ActorBlock; ///-----------------------------------------------------------------/// // Mesh attributes. diff --git a/Rendering/WebGPU/wgsl/LineRoundJoinVertexShader.wgsl b/Rendering/WebGPU/wgsl/LineRoundJoinVertexShader.wgsl index 9e81b90a26d..a0773d1693f 100644 --- a/Rendering/WebGPU/wgsl/LineRoundJoinVertexShader.wgsl +++ b/Rendering/WebGPU/wgsl/LineRoundJoinVertexShader.wgsl @@ -138,7 +138,7 @@ struct Topology { ///------------------------------------------------------------------- // Everything shader needs from the vtkActor and it's vtkProperty ///------------------------------------------------------------------- -@group(1) @binding(0) var<uniform> actor: ActorBlock; +@group(1) @binding(0) var<storage, read> actor: ActorBlock; ///-----------------------------------------------------------------/// // Mesh attributes. diff --git a/Rendering/WebGPU/wgsl/PointShader.wgsl b/Rendering/WebGPU/wgsl/PointShader.wgsl index 33f217c7c99..193197ff5f9 100644 --- a/Rendering/WebGPU/wgsl/PointShader.wgsl +++ b/Rendering/WebGPU/wgsl/PointShader.wgsl @@ -89,7 +89,7 @@ struct Topology { ///------------------------------------------------------------------- // Everything shader needs from the vtkActor and it's vtkProperty ///------------------------------------------------------------------- -@group(1) @binding(0) var<uniform> actor: ActorBlock; +@group(1) @binding(0) var<storage, read> actor: ActorBlock; ///-----------------------------------------------------------------/// // Mesh attributes. diff --git a/Rendering/WebGPU/wgsl/SurfaceMeshShader.wgsl b/Rendering/WebGPU/wgsl/SurfaceMeshShader.wgsl index c55ba3bfed2..b9186314859 100644 --- a/Rendering/WebGPU/wgsl/SurfaceMeshShader.wgsl +++ b/Rendering/WebGPU/wgsl/SurfaceMeshShader.wgsl @@ -70,7 +70,7 @@ struct Topology { ///------------------------------------------------------------------- // Everything shader needs from the vtkActor and it's vtkProperty ///------------------------------------------------------------------- -@group(1) @binding(0) var<uniform> actor: ActorBlock; +@group(1) @binding(0) var<storage, read> actor: ActorBlock; ///-----------------------------------------------------------------/// // Mesh attributes. -- GitLab