1 // Copyright 2019 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef DAWNNATIVE_BINDGROUPTRACKER_H_ 16 #define DAWNNATIVE_BINDGROUPTRACKER_H_ 17 18 #include "common/Constants.h" 19 #include "dawn_native/BindGroupLayout.h" 20 #include "dawn_native/Pipeline.h" 21 #include "dawn_native/PipelineLayout.h" 22 23 #include <array> 24 #include <bitset> 25 26 namespace dawn_native { 27 28 // Keeps track of the dirty bind groups so they can be lazily applied when we know the 29 // pipeline state or it changes. 30 // |DynamicOffset| is a template parameter because offsets in Vulkan are uint32_t but uint64_t 31 // in other backends. 32 template <bool CanInheritBindGroups, typename DynamicOffset> 33 class BindGroupTrackerBase { 34 public: OnSetBindGroup(BindGroupIndex index,BindGroupBase * bindGroup,uint32_t dynamicOffsetCount,uint32_t * dynamicOffsets)35 void OnSetBindGroup(BindGroupIndex index, 36 BindGroupBase* bindGroup, 37 uint32_t dynamicOffsetCount, 38 uint32_t* dynamicOffsets) { 39 ASSERT(index < kMaxBindGroupsTyped); 40 41 if (mBindGroupLayoutsMask[index]) { 42 // It is okay to only dirty bind groups that are used by the current pipeline 43 // layout. If the pipeline layout changes, then the bind groups it uses will 44 // become dirty. 45 46 if (mBindGroups[index] != bindGroup) { 47 mDirtyBindGroups.set(index); 48 mDirtyBindGroupsObjectChangedOrIsDynamic.set(index); 49 } 50 51 if (dynamicOffsetCount > 0) { 52 mDirtyBindGroupsObjectChangedOrIsDynamic.set(index); 53 } 54 } 55 56 mBindGroups[index] = bindGroup; 57 mDynamicOffsetCounts[index] = dynamicOffsetCount; 58 SetDynamicOffsets(mDynamicOffsets[index].data(), dynamicOffsetCount, dynamicOffsets); 59 } 60 OnSetPipeline(PipelineBase * pipeline)61 void OnSetPipeline(PipelineBase* pipeline) { 62 mPipelineLayout = pipeline->GetLayout(); 63 } 64 65 protected: 66 // The Derived class should call this before it applies bind groups. BeforeApply()67 void BeforeApply() { 68 if (mLastAppliedPipelineLayout == mPipelineLayout) { 69 return; 70 } 71 72 // Use the bind group layout mask to avoid marking unused bind groups as dirty. 73 mBindGroupLayoutsMask = mPipelineLayout->GetBindGroupLayoutsMask(); 74 75 // Changing the pipeline layout sets bind groups as dirty. If CanInheritBindGroups, 76 // the first |k| matching bind groups may be inherited. 77 if (CanInheritBindGroups && mLastAppliedPipelineLayout != nullptr) { 78 // Dirty bind groups that cannot be inherited. 79 BindGroupLayoutMask dirtiedGroups = 80 ~mPipelineLayout->InheritedGroupsMask(mLastAppliedPipelineLayout); 81 82 mDirtyBindGroups |= dirtiedGroups; 83 mDirtyBindGroupsObjectChangedOrIsDynamic |= dirtiedGroups; 84 85 // Clear any bind groups not in the mask. 86 mDirtyBindGroups &= mBindGroupLayoutsMask; 87 mDirtyBindGroupsObjectChangedOrIsDynamic &= mBindGroupLayoutsMask; 88 } else { 89 mDirtyBindGroups = mBindGroupLayoutsMask; 90 mDirtyBindGroupsObjectChangedOrIsDynamic = mBindGroupLayoutsMask; 91 } 92 } 93 94 // The Derived class should call this after it applies bind groups. AfterApply()95 void AfterApply() { 96 // Reset all dirty bind groups. Dirty bind groups not in the bind group layout mask 97 // will be dirtied again by the next pipeline change. 98 mDirtyBindGroups.reset(); 99 mDirtyBindGroupsObjectChangedOrIsDynamic.reset(); 100 // Keep track of the last applied pipeline layout. This allows us to avoid computing 101 // the intersection of the dirty bind groups and bind group layout mask in next Draw 102 // or Dispatch (which is very hot code) until the layout is changed again. 103 mLastAppliedPipelineLayout = mPipelineLayout; 104 } 105 106 BindGroupLayoutMask mDirtyBindGroups = 0; 107 BindGroupLayoutMask mDirtyBindGroupsObjectChangedOrIsDynamic = 0; 108 BindGroupLayoutMask mBindGroupLayoutsMask = 0; 109 ityp::array<BindGroupIndex, BindGroupBase*, kMaxBindGroups> mBindGroups = {}; 110 ityp::array<BindGroupIndex, uint32_t, kMaxBindGroups> mDynamicOffsetCounts = {}; 111 ityp::array<BindGroupIndex, 112 std::array<DynamicOffset, kMaxDynamicBuffersPerPipelineLayout>, 113 kMaxBindGroups> 114 mDynamicOffsets = {}; 115 116 // |mPipelineLayout| is the current pipeline layout set on the command buffer. 117 // |mLastAppliedPipelineLayout| is the last pipeline layout for which we applied changes 118 // to the bind group bindings. 119 PipelineLayoutBase* mPipelineLayout = nullptr; 120 PipelineLayoutBase* mLastAppliedPipelineLayout = nullptr; 121 122 private: 123 // We have two overloads here because offsets in Vulkan are uint32_t but uint64_t 124 // in other backends. SetDynamicOffsets(uint64_t * data,uint32_t dynamicOffsetCount,uint32_t * dynamicOffsets)125 static void SetDynamicOffsets(uint64_t* data, 126 uint32_t dynamicOffsetCount, 127 uint32_t* dynamicOffsets) { 128 for (uint32_t i = 0; i < dynamicOffsetCount; ++i) { 129 data[i] = static_cast<uint64_t>(dynamicOffsets[i]); 130 } 131 } 132 SetDynamicOffsets(uint32_t * data,uint32_t dynamicOffsetCount,uint32_t * dynamicOffsets)133 static void SetDynamicOffsets(uint32_t* data, 134 uint32_t dynamicOffsetCount, 135 uint32_t* dynamicOffsets) { 136 memcpy(data, dynamicOffsets, sizeof(uint32_t) * dynamicOffsetCount); 137 } 138 }; 139 140 } // namespace dawn_native 141 142 #endif // DAWNNATIVE_BINDGROUPTRACKER_H_ 143