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