1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "media/fuchsia/common/sysmem_buffer_writer.h"
6 
7 #include <zircon/rights.h>
8 #include <algorithm>
9 
10 #include "base/bits.h"
11 #include "base/fuchsia/fuchsia_logging.h"
12 #include "base/process/process_metrics.h"
13 
14 namespace media {
15 
16 class SysmemBufferWriter::Buffer {
17  public:
18   Buffer() = default;
19 
~Buffer()20   ~Buffer() {
21     if (!base_address_) {
22       return;
23     }
24 
25     size_t mapped_bytes =
26         base::bits::Align(offset_ + size_, base::GetPageSize());
27     zx_status_t status = zx::vmar::root_self()->unmap(
28         reinterpret_cast<uintptr_t>(base_address_), mapped_bytes);
29     ZX_DCHECK(status == ZX_OK, status) << "zx_vmar_unmap";
30   }
31 
32   Buffer(Buffer&&) = default;
33   Buffer& operator=(Buffer&&) = default;
34 
Initialize(zx::vmo vmo,size_t offset,size_t size,fuchsia::sysmem::CoherencyDomain coherency_domain)35   bool Initialize(zx::vmo vmo,
36                   size_t offset,
37                   size_t size,
38                   fuchsia::sysmem::CoherencyDomain coherency_domain) {
39     DCHECK(!base_address_);
40     DCHECK(vmo);
41 
42     // zx_vmo_write() doesn't work for sysmem-allocated VMOs (see ZX-4854), so
43     // the VMOs have to be mapped.
44     size_t bytes_to_map = base::bits::Align(offset + size, base::GetPageSize());
45     uintptr_t addr;
46     zx_status_t status = zx::vmar::root_self()->map(
47         /*vmar_offset=*/0, vmo, /*vmo_offset=*/0, bytes_to_map,
48         ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, &addr);
49     if (status != ZX_OK) {
50       ZX_DLOG(ERROR, status) << "zx_vmar_map";
51       return false;
52     }
53 
54     base_address_ = reinterpret_cast<uint8_t*>(addr);
55     offset_ = offset;
56     size_ = size;
57     coherency_domain_ = coherency_domain;
58 
59     return true;
60   }
61 
is_used() const62   bool is_used() const { return is_used_; }
size() const63   size_t size() const { return size_; }
64 
65   // Copies as much data as possible from |data| to this input buffer.
Write(base::span<const uint8_t> data)66   size_t Write(base::span<const uint8_t> data) {
67     DCHECK(!is_used_);
68     is_used_ = true;
69 
70     size_t bytes_to_fill = std::min(size_, data.size());
71     memcpy(base_address_ + offset_, data.data(), bytes_to_fill);
72 
73     // Flush CPU cache if StreamProcessor reads from RAM.
74     if (coherency_domain_ == fuchsia::sysmem::CoherencyDomain::RAM) {
75       zx_status_t status = zx_cache_flush(base_address_ + offset_,
76                                           bytes_to_fill, ZX_CACHE_FLUSH_DATA);
77       ZX_DCHECK(status == ZX_OK, status) << "zx_cache_flush";
78     }
79 
80     return bytes_to_fill;
81   }
82 
Release()83   void Release() { is_used_ = false; }
84 
85  private:
86   uint8_t* base_address_ = nullptr;
87 
88   // Buffer settings provided by sysmem.
89   size_t offset_ = 0;
90   size_t size_ = 0;
91   fuchsia::sysmem::CoherencyDomain coherency_domain_;
92 
93   // Set to true when this buffer is being used by the codec.
94   bool is_used_ = false;
95 };
96 
SysmemBufferWriter(std::vector<Buffer> buffers)97 SysmemBufferWriter::SysmemBufferWriter(std::vector<Buffer> buffers)
98     : buffers_(std::move(buffers)) {}
99 
100 SysmemBufferWriter::~SysmemBufferWriter() = default;
101 
Write(size_t index,base::span<const uint8_t> data)102 size_t SysmemBufferWriter::Write(size_t index, base::span<const uint8_t> data) {
103   DCHECK_LT(index, buffers_.size());
104   DCHECK(!buffers_[index].is_used());
105 
106   return buffers_[index].Write(data);
107 }
108 
Acquire()109 base::Optional<size_t> SysmemBufferWriter::Acquire() {
110   auto it = std::find_if(
111       buffers_.begin(), buffers_.end(),
112       [](const SysmemBufferWriter::Buffer& buf) { return !buf.is_used(); });
113 
114   if (it == buffers_.end())
115     return base::nullopt;
116 
117   return it - buffers_.begin();
118 }
119 
Release(size_t index)120 void SysmemBufferWriter::Release(size_t index) {
121   DCHECK_LT(index, buffers_.size());
122   buffers_[index].Release();
123 }
124 
ReleaseAll()125 void SysmemBufferWriter::ReleaseAll() {
126   for (auto& buf : buffers_) {
127     buf.Release();
128   }
129 }
130 
num_buffers() const131 size_t SysmemBufferWriter::num_buffers() const {
132   return buffers_.size();
133 }
134 
135 // static
Create(fuchsia::sysmem::BufferCollectionInfo_2 info)136 std::unique_ptr<SysmemBufferWriter> SysmemBufferWriter::Create(
137     fuchsia::sysmem::BufferCollectionInfo_2 info) {
138   std::vector<SysmemBufferWriter::Buffer> buffers;
139   buffers.resize(info.buffer_count);
140 
141   fuchsia::sysmem::BufferMemorySettings& settings =
142       info.settings.buffer_settings;
143   for (size_t i = 0; i < info.buffer_count; ++i) {
144     fuchsia::sysmem::VmoBuffer& buffer = info.buffers[i];
145     if (!buffers[i].Initialize(std::move(buffer.vmo), buffer.vmo_usable_start,
146                                settings.size_bytes,
147                                settings.coherency_domain)) {
148       return nullptr;
149     }
150   }
151 
152   return std::make_unique<SysmemBufferWriter>(std::move(buffers));
153 }
154 
155 // static
156 base::Optional<fuchsia::sysmem::BufferCollectionConstraints>
GetRecommendedConstraints(const fuchsia::media::StreamBufferConstraints & stream_constraints)157 SysmemBufferWriter::GetRecommendedConstraints(
158     const fuchsia::media::StreamBufferConstraints& stream_constraints) {
159   fuchsia::sysmem::BufferCollectionConstraints buffer_constraints;
160 
161   if (!stream_constraints.has_default_settings() ||
162       !stream_constraints.default_settings().has_packet_count_for_client()) {
163     DLOG(ERROR)
164         << "Received StreamBufferConstaints with missing required fields.";
165     return base::nullopt;
166   }
167 
168   // Currently we have to map buffers VMOs to write to them (see ZX-4854) and
169   // memory cannot be mapped as write-only (see ZX-4872), so request RW access
170   // even though we will never need to read from these buffers.
171   buffer_constraints.usage.cpu =
172       fuchsia::sysmem::cpuUsageRead | fuchsia::sysmem::cpuUsageWrite;
173 
174   buffer_constraints.min_buffer_count_for_camping =
175       stream_constraints.default_settings().packet_count_for_client();
176   buffer_constraints.has_buffer_memory_constraints = true;
177 
178   const int kDefaultPacketSize = 512 * 1024;
179   buffer_constraints.buffer_memory_constraints.min_size_bytes =
180       stream_constraints.has_per_packet_buffer_bytes_recommended()
181           ? stream_constraints.per_packet_buffer_bytes_recommended()
182           : kDefaultPacketSize;
183 
184   buffer_constraints.buffer_memory_constraints.ram_domain_supported = true;
185   buffer_constraints.buffer_memory_constraints.cpu_domain_supported = true;
186 
187   return buffer_constraints;
188 }
189 
190 }  // namespace media
191