• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "CommandBufferStagingStream.h"
17 
18 #include <errno.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 
24 #include <atomic>
25 #include <vector>
26 
27 #include "util/log.h"
28 
29 static const size_t kReadSize = 512 * 1024;
30 static const size_t kWriteOffset = kReadSize;
31 
32 namespace gfxstream {
33 namespace vk {
34 
CommandBufferStagingStream()35 CommandBufferStagingStream::CommandBufferStagingStream()
36     : IOStream(1048576), m_size(0), m_writePos(0) {
37     // use default allocators
38     m_alloc = [](size_t size) -> Memory {
39         return {
40             .deviceMemory = VK_NULL_HANDLE,  // no device memory for malloc
41             .ptr = malloc(size),
42         };
43     };
44     m_free = [](const Memory& mem) { free(mem.ptr); };
45     m_realloc = [](const Memory& mem, size_t size) -> Memory {
46         return {.deviceMemory = VK_NULL_HANDLE, .ptr = realloc(mem.ptr, size)};
47     };
48 }
49 
CommandBufferStagingStream(const Alloc & allocFn,const Free & freeFn)50 CommandBufferStagingStream::CommandBufferStagingStream(const Alloc& allocFn, const Free& freeFn)
51     : CommandBufferStagingStream() {
52     m_usingCustomAlloc = true;
53     // for custom allocation, allocate metadata memory at the beginning.
54     // m_alloc, m_free and m_realloc wraps sync data logic
55 
56     // \param size to allocate
57     // \return ptr starting at data
58     m_alloc = [&allocFn](size_t size) -> Memory {
59         // allocation requested size + sync data size
60 
61         // <---sync bytes--><----Data--->
62         // |———————————————|————————————|
63         // |0|1|2|3|4|5|6|7|............|
64         // |———————————————|————————————|
65         // ꜛ               ꜛ
66         // allocated ptr   ptr to data [dataPtr]
67 
68         Memory memory;
69         if (!allocFn) {
70             mesa_loge("Custom allocation (%zu bytes) failed\n", size);
71             return memory;
72         }
73 
74         // custom allocation/free requires metadata for sync between host/guest
75         const size_t totalSize = size + kSyncDataSize;
76         memory = allocFn(totalSize);
77         if (!memory.ptr) {
78             mesa_loge("Custom allocation (%zu bytes) failed\n", size);
79             return memory;
80         }
81 
82         // set sync data to read complete
83         uint32_t* syncDWordPtr = reinterpret_cast<uint32_t*>(memory.ptr);
84         __atomic_store_n(syncDWordPtr, kSyncDataReadComplete, __ATOMIC_RELEASE);
85         return memory;
86     };
87 
88     m_free = [&freeFn](const Memory& mem) {
89         if (!freeFn) {
90             mesa_loge("Custom free for memory(%p) failed\n", mem.ptr);
91             return;
92         }
93         freeFn(mem);
94     };
95 
96     // \param ptr is the data pointer currently allocated
97     // \return dataPtr starting at data
98     m_realloc = [this](const Memory& mem, size_t size) -> Memory {
99         // realloc requires freeing previously allocated memory
100         // read sync DWORD to ensure host is done reading this memory
101         // before releasing it.
102 
103         size_t hostWaits = 0;
104 
105         uint32_t* syncDWordPtr = reinterpret_cast<uint32_t*>(mem.ptr);
106         while (__atomic_load_n(syncDWordPtr, __ATOMIC_ACQUIRE) != kSyncDataReadComplete) {
107             hostWaits++;
108             usleep(10);
109             if (hostWaits > 1000) {
110                 mesa_logd("%s: warning, stalled on host decoding on this command buffer stream\n",
111                           __func__);
112             }
113         }
114 
115         // for custom allocation/free, memory holding metadata must be copied
116         // along with stream data
117         // <---sync bytes--><----Data--->
118         // |———————————————|————————————|
119         // |0|1|2|3|4|5|6|7|............|
120         // |———————————————|————————————|
121         // ꜛ               ꜛ
122         // [copyLocation]  ptr to data [ptr]
123 
124         const size_t toCopySize = m_writePos + kSyncDataSize;
125         unsigned char* copyLocation = static_cast<unsigned char*>(mem.ptr);
126         std::vector<uint8_t> tmp(copyLocation, copyLocation + toCopySize);
127         m_free(mem);
128 
129         // get new buffer and copy previous stream data to it
130         Memory newMemory = m_alloc(size);
131         unsigned char* newBuf = static_cast<unsigned char*>(newMemory.ptr);
132         if (!newBuf) {
133             mesa_loge("Custom allocation (%zu bytes) failed\n", size);
134             return newMemory;
135         }
136         // copy previous data
137         memcpy(newBuf, tmp.data(), toCopySize);
138 
139         return newMemory;
140     };
141 }
142 
~CommandBufferStagingStream()143 CommandBufferStagingStream::~CommandBufferStagingStream() {
144     flush();
145     if (m_mem.ptr) m_free(m_mem);
146 }
147 
getDataPtr()148 unsigned char* CommandBufferStagingStream::getDataPtr() {
149     if (!m_mem.ptr) return nullptr;
150     const size_t metadataSize = m_usingCustomAlloc ? kSyncDataSize : 0;
151     return static_cast<unsigned char*>(m_mem.ptr) + metadataSize;
152 }
153 
markFlushing()154 void CommandBufferStagingStream::markFlushing() {
155     if (!m_usingCustomAlloc) {
156         return;
157     }
158     uint32_t* syncDWordPtr = reinterpret_cast<uint32_t*>(m_mem.ptr);
159     __atomic_store_n(syncDWordPtr, kSyncDataReadPending, __ATOMIC_RELEASE);
160 }
161 
idealAllocSize(size_t len)162 size_t CommandBufferStagingStream::idealAllocSize(size_t len) {
163     if (len > 1048576) return len;
164     return 1048576;
165 }
166 
allocBuffer(size_t minSize)167 void* CommandBufferStagingStream::allocBuffer(size_t minSize) {
168     size_t allocSize = (1048576 < minSize ? minSize : 1048576);
169     // Initial case: blank
170     if (!m_mem.ptr) {
171         m_mem = m_alloc(allocSize);
172         m_size = allocSize;
173         return getDataPtr();
174     }
175 
176     // Calculate remaining
177     size_t remaining = m_size - m_writePos;
178     // check if there is at least minSize bytes left in buffer
179     // if not, reallocate a buffer of big enough size
180     if (remaining < minSize) {
181         size_t newAllocSize = m_size * 2 + allocSize;
182         m_mem = m_realloc(m_mem, newAllocSize);
183         m_size = newAllocSize;
184 
185         return (void*)(getDataPtr() + m_writePos);
186     }
187 
188     // for custom allocations, host should have finished reading
189     // data from command buffer since command buffers are flushed
190     // on queue submit.
191     // allocBuffer should not be called on command buffers that are currently
192     // being read by the host
193     if (m_usingCustomAlloc) {
194         uint32_t* syncDWordPtr = reinterpret_cast<uint32_t*>(m_mem.ptr);
195         if (__atomic_load_n(syncDWordPtr, __ATOMIC_ACQUIRE) != kSyncDataReadComplete) {
196             mesa_loge("FATAL: allocBuffer() called but previous read not complete");
197             abort();
198         }
199     }
200 
201     return (void*)(getDataPtr() + m_writePos);
202 }
203 
commitBuffer(size_t size)204 int CommandBufferStagingStream::commitBuffer(size_t size) {
205     m_writePos += size;
206     return 0;
207 }
208 
readFully(void *,size_t)209 const unsigned char* CommandBufferStagingStream::readFully(void*, size_t) {
210     // Not supported
211     mesa_loge("CommandBufferStagingStream::%s: Fatal: not supported\n", __func__);
212     abort();
213     return nullptr;
214 }
215 
read(void *,size_t *)216 const unsigned char* CommandBufferStagingStream::read(void*, size_t*) {
217     // Not supported
218     mesa_loge("CommandBufferStagingStream::%s: Fatal: not supported\n", __func__);
219     abort();
220     return nullptr;
221 }
222 
writeFully(const void *,size_t)223 int CommandBufferStagingStream::writeFully(const void*, size_t) {
224     // Not supported
225     mesa_loge("CommandBufferStagingStream::%s: Fatal: not supported\n", __func__);
226     abort();
227     return 0;
228 }
229 
commitBufferAndReadFully(size_t,void *,size_t)230 const unsigned char* CommandBufferStagingStream::commitBufferAndReadFully(size_t, void*, size_t) {
231     // Not supported
232     mesa_loge("CommandBufferStagingStream::%s: Fatal: not supported\n", __func__);
233     abort();
234     return nullptr;
235 }
236 
getWritten(unsigned char ** bufOut,size_t * sizeOut)237 void CommandBufferStagingStream::getWritten(unsigned char** bufOut, size_t* sizeOut) {
238     *bufOut = getDataPtr();
239     *sizeOut = m_writePos;
240 }
241 
reset()242 void CommandBufferStagingStream::reset() {
243     m_writePos = 0;
244     IOStream::rewind();
245 }
246 
getDeviceMemory()247 VkDeviceMemory CommandBufferStagingStream::getDeviceMemory() { return m_mem.deviceMemory; }
248 
249 }  // namespace vk
250 }  // namespace gfxstream
251