1 // 2 // Copyright 2022 The ANGLE Project Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style license that can be 4 // found in the LICENSE file. 5 // 6 // AllocatorHelperRing: 7 // Manages the ring buffer allocators used in the command buffers. 8 // 9 10 #ifndef LIBANGLE_RENDERER_VULKAN_ALLOCATORHELPERRING_H_ 11 #define LIBANGLE_RENDERER_VULKAN_ALLOCATORHELPERRING_H_ 12 13 #include "common/RingBufferAllocator.h" 14 #include "common/vulkan/vk_headers.h" 15 #include "libANGLE/renderer/vulkan/vk_command_buffer_utils.h" 16 #include "libANGLE/renderer/vulkan/vk_wrapper.h" 17 18 namespace rx 19 { 20 namespace vk 21 { 22 namespace priv 23 { 24 class SecondaryCommandBuffer; 25 } // namespace priv 26 27 using SharedCommandMemoryAllocator = angle::SharedRingBufferAllocator; 28 using RingBufferAllocator = angle::RingBufferAllocator; 29 30 // Used in CommandBufferHelperCommon 31 class SharedCommandBlockAllocator 32 { 33 public: SharedCommandBlockAllocator()34 SharedCommandBlockAllocator() : mAllocator(nullptr), mAllocSharedCP(nullptr) {} 35 void resetAllocator(); hasAllocatorLinks()36 bool hasAllocatorLinks() const { return mAllocator || mAllocSharedCP; } 37 init()38 void init() {} 39 40 void attachAllocator(SharedCommandMemoryAllocator *allocator); 41 SharedCommandMemoryAllocator *detachAllocator(bool isCommandBufferEmpty); 42 getAllocator()43 SharedCommandMemoryAllocator *getAllocator() { return mAllocator; } 44 45 private: 46 // Using a ring buffer allocator for less memory overhead (observed with the async queue 47 // ex-feature) 48 SharedCommandMemoryAllocator *mAllocator; 49 angle::SharedRingBufferAllocatorCheckPoint *mAllocSharedCP; 50 angle::RingBufferAllocatorCheckPoint mAllocReleaseCP; 51 }; 52 53 // Used in SecondaryCommandBuffer 54 class SharedCommandBlockPool final : angle::RingBufferAllocateListener 55 { 56 public: SharedCommandBlockPool()57 SharedCommandBlockPool() 58 : mLastCommandBlock(nullptr), mFinishedCommandSize(0), mCommandBuffer(nullptr) 59 {} 60 61 static constexpr size_t kCommandHeaderSize = 4; 62 using CommandHeaderIDType = uint16_t; 63 // Make sure the size of command header ID type is less than total command header size. 64 static_assert(sizeof(CommandHeaderIDType) < kCommandHeaderSize, "Check size of CommandHeader"); 65 setCommandBuffer(priv::SecondaryCommandBuffer * commandBuffer)66 void setCommandBuffer(priv::SecondaryCommandBuffer *commandBuffer) 67 { 68 mCommandBuffer = commandBuffer; 69 } resetCommandBuffer()70 void resetCommandBuffer() { mCommandBuffer = nullptr; } 71 reset(CommandBufferCommandTracker * commandBufferTracker)72 void reset(CommandBufferCommandTracker *commandBufferTracker) 73 { 74 mLastCommandBlock = nullptr; 75 mFinishedCommandSize = 0; 76 if (mAllocator.valid()) 77 { 78 mAllocator.release(mAllocator.getReleaseCheckPoint()); 79 pushNewCommandBlock(mAllocator.allocate(0)); 80 } 81 } 82 initialize(SharedCommandMemoryAllocator * allocator)83 angle::Result initialize(SharedCommandMemoryAllocator *allocator) 84 { 85 return angle::Result::Continue; 86 } 87 88 // Always valid (even if allocator is detached). valid()89 bool valid() const { return true; } empty()90 bool empty() const { return getCommandSize() == 0; } 91 92 void getMemoryUsageStats(size_t *usedMemoryOut, size_t *allocatedMemoryOut) const; 93 terminateLastCommandBlock()94 void terminateLastCommandBlock() 95 { 96 if (mLastCommandBlock) 97 { 98 ASSERT(mAllocator.valid()); 99 ASSERT(mAllocator.getPointer() >= mLastCommandBlock); 100 ASSERT(mAllocator.getFragmentSize() >= kCommandHeaderSize); 101 reinterpret_cast<CommandHeaderIDType &>(*(mAllocator.getPointer())) = 0; 102 } 103 } 104 105 // Initialize the SecondaryCommandBuffer by setting the allocator it will use 106 void attachAllocator(SharedCommandMemoryAllocator *source); 107 void detachAllocator(SharedCommandMemoryAllocator *destination); 108 onNewVariableSizedCommand(const size_t requiredSize,const size_t allocationSize,uint8_t ** headerOut)109 void onNewVariableSizedCommand(const size_t requiredSize, 110 const size_t allocationSize, 111 uint8_t **headerOut) 112 { 113 *headerOut = allocateCommand(allocationSize); 114 } onNewCommand(const size_t requiredSize,const size_t allocationSize,uint8_t ** headerOut)115 void onNewCommand(const size_t requiredSize, const size_t allocationSize, uint8_t **headerOut) 116 { 117 *headerOut = allocateCommand(allocationSize); 118 } 119 120 private: allocateCommand(size_t allocationSize)121 uint8_t *allocateCommand(size_t allocationSize) 122 { 123 ASSERT(mLastCommandBlock); 124 return mAllocator.allocate(static_cast<uint32_t>(allocationSize)); 125 } 126 127 // The following is used to give the size of the command buffer in bytes getCommandSize()128 uint32_t getCommandSize() const 129 { 130 uint32_t result = mFinishedCommandSize; 131 if (mLastCommandBlock) 132 { 133 ASSERT(mAllocator.valid()); 134 ASSERT(mAllocator.getPointer() >= mLastCommandBlock); 135 result += static_cast<uint32_t>(mAllocator.getPointer() - mLastCommandBlock); 136 } 137 return result; 138 } 139 140 void pushNewCommandBlock(uint8_t *block); 141 void finishLastCommandBlock(); 142 143 // Functions derived from RingBufferAllocateListener 144 virtual void onRingBufferNewFragment() override; 145 virtual void onRingBufferFragmentEnd() override; 146 147 // Using a ring buffer allocator for less memory overhead (observed with the async queue 148 // ex-feature) 149 RingBufferAllocator mAllocator; 150 uint8_t *mLastCommandBlock; 151 uint32_t mFinishedCommandSize; 152 153 // Points to the parent command buffer. 154 priv::SecondaryCommandBuffer *mCommandBuffer; 155 }; 156 157 } // namespace vk 158 } // namespace rx 159 160 #endif // LIBANGLE_RENDERER_VULKAN_ALLOCATORHELPERRING_H_ 161