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 enabled async queue) 47 SharedCommandMemoryAllocator *mAllocator; 48 angle::SharedRingBufferAllocatorCheckPoint *mAllocSharedCP; 49 angle::RingBufferAllocatorCheckPoint mAllocReleaseCP; 50 }; 51 52 // Used in SecondaryCommandBuffer 53 class SharedCommandBlockPool final : angle::RingBufferAllocateListener 54 { 55 public: SharedCommandBlockPool()56 SharedCommandBlockPool() 57 : mLastCommandBlock(nullptr), mFinishedCommandSize(0), mCommandBuffer(nullptr) 58 {} 59 60 static constexpr size_t kCommandHeaderSize = 4; 61 using CommandHeaderIDType = uint16_t; 62 // Make sure the size of command header ID type is less than total command header size. 63 static_assert(sizeof(CommandHeaderIDType) < kCommandHeaderSize, "Check size of CommandHeader"); 64 setCommandBuffer(priv::SecondaryCommandBuffer * commandBuffer)65 void setCommandBuffer(priv::SecondaryCommandBuffer *commandBuffer) 66 { 67 mCommandBuffer = commandBuffer; 68 } resetCommandBuffer()69 void resetCommandBuffer() { mCommandBuffer = nullptr; } 70 reset(CommandBufferCommandTracker * commandBufferTracker)71 void reset(CommandBufferCommandTracker *commandBufferTracker) 72 { 73 mLastCommandBlock = nullptr; 74 mFinishedCommandSize = 0; 75 if (mAllocator.valid()) 76 { 77 mAllocator.release(mAllocator.getReleaseCheckPoint()); 78 pushNewCommandBlock(mAllocator.allocate(0)); 79 } 80 } 81 initialize(SharedCommandMemoryAllocator * allocator)82 angle::Result initialize(SharedCommandMemoryAllocator *allocator) 83 { 84 return angle::Result::Continue; 85 } 86 87 // Always valid (even if allocator is detached). valid()88 bool valid() const { return true; } empty()89 bool empty() const { return getCommandSize() == 0; } 90 91 void getMemoryUsageStats(size_t *usedMemoryOut, size_t *allocatedMemoryOut) const; 92 terminateLastCommandBlock()93 void terminateLastCommandBlock() 94 { 95 if (mLastCommandBlock) 96 { 97 ASSERT(mAllocator.valid()); 98 ASSERT(mAllocator.getPointer() >= mLastCommandBlock); 99 ASSERT(mAllocator.getFragmentSize() >= kCommandHeaderSize); 100 reinterpret_cast<CommandHeaderIDType &>(*(mAllocator.getPointer())) = 0; 101 } 102 } 103 104 // Initialize the SecondaryCommandBuffer by setting the allocator it will use 105 void attachAllocator(SharedCommandMemoryAllocator *source); 106 void detachAllocator(SharedCommandMemoryAllocator *destination); 107 onNewVariableSizedCommand(const size_t requiredSize,const size_t allocationSize,uint8_t ** headerOut)108 void onNewVariableSizedCommand(const size_t requiredSize, 109 const size_t allocationSize, 110 uint8_t **headerOut) 111 { 112 *headerOut = allocateCommand(allocationSize); 113 } onNewCommand(const size_t requiredSize,const size_t allocationSize,uint8_t ** headerOut)114 void onNewCommand(const size_t requiredSize, const size_t allocationSize, uint8_t **headerOut) 115 { 116 *headerOut = allocateCommand(allocationSize); 117 } 118 119 private: allocateCommand(size_t allocationSize)120 uint8_t *allocateCommand(size_t allocationSize) 121 { 122 ASSERT(mLastCommandBlock); 123 return mAllocator.allocate(static_cast<uint32_t>(allocationSize)); 124 } 125 126 // The following is used to give the size of the command buffer in bytes getCommandSize()127 uint32_t getCommandSize() const 128 { 129 uint32_t result = mFinishedCommandSize; 130 if (mLastCommandBlock) 131 { 132 ASSERT(mAllocator.valid()); 133 ASSERT(mAllocator.getPointer() >= mLastCommandBlock); 134 result += static_cast<uint32_t>(mAllocator.getPointer() - mLastCommandBlock); 135 } 136 return result; 137 } 138 139 void pushNewCommandBlock(uint8_t *block); 140 void finishLastCommandBlock(); 141 142 // Functions derived from RingBufferAllocateListener 143 virtual void onRingBufferNewFragment() override; 144 virtual void onRingBufferFragmentEnd() override; 145 146 // Using a ring buffer allocator for less memory overhead (observed with enabled async queue) 147 RingBufferAllocator mAllocator; 148 uint8_t *mLastCommandBlock; 149 uint32_t mFinishedCommandSize; 150 151 // Points to the parent command buffer. 152 priv::SecondaryCommandBuffer *mCommandBuffer; 153 }; 154 155 } // namespace vk 156 } // namespace rx 157 158 #endif // LIBANGLE_RENDERER_VULKAN_ALLOCATORHELPERRING_H_ 159