• 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 <cutils/log.h>
19 #include <cutils/properties.h>
20 #include <errno.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #include <atomic>
27 #include <vector>
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             ALOGE("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             ALOGE("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             ALOGE("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                 ALOGD("%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             ALOGE("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         LOG_ALWAYS_FATAL_IF(
196             __atomic_load_n(syncDWordPtr, __ATOMIC_ACQUIRE) != kSyncDataReadComplete,
197             "FATAL: allocBuffer() called but previous read not complete");
198     }
199 
200     return (void*)(getDataPtr() + m_writePos);
201 }
202 
commitBuffer(size_t size)203 int CommandBufferStagingStream::commitBuffer(size_t size) {
204     m_writePos += size;
205     return 0;
206 }
207 
readFully(void *,size_t)208 const unsigned char* CommandBufferStagingStream::readFully(void*, size_t) {
209     // Not supported
210     ALOGE("CommandBufferStagingStream::%s: Fatal: not supported\n", __func__);
211     abort();
212     return nullptr;
213 }
214 
read(void *,size_t *)215 const unsigned char* CommandBufferStagingStream::read(void*, size_t*) {
216     // Not supported
217     ALOGE("CommandBufferStagingStream::%s: Fatal: not supported\n", __func__);
218     abort();
219     return nullptr;
220 }
221 
writeFully(const void *,size_t)222 int CommandBufferStagingStream::writeFully(const void*, size_t) {
223     // Not supported
224     ALOGE("CommandBufferStagingStream::%s: Fatal: not supported\n", __func__);
225     abort();
226     return 0;
227 }
228 
commitBufferAndReadFully(size_t,void *,size_t)229 const unsigned char* CommandBufferStagingStream::commitBufferAndReadFully(size_t, void*, size_t) {
230     // Not supported
231     ALOGE("CommandBufferStagingStream::%s: Fatal: not supported\n", __func__);
232     abort();
233     return nullptr;
234 }
235 
getWritten(unsigned char ** bufOut,size_t * sizeOut)236 void CommandBufferStagingStream::getWritten(unsigned char** bufOut, size_t* sizeOut) {
237     *bufOut = getDataPtr();
238     *sizeOut = m_writePos;
239 }
240 
reset()241 void CommandBufferStagingStream::reset() {
242     m_writePos = 0;
243     IOStream::rewind();
244 }
245 
getDeviceMemory()246 VkDeviceMemory CommandBufferStagingStream::getDeviceMemory() { return m_mem.deviceMemory; }
247 
248 }  // namespace vk
249 }  // namespace gfxstream
250