1/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
2*
3* Redistribution and use in source and binary forms, with or without
4* modification, are permitted provided that the following conditions are
5* met:
6*  * Redistributions of source code must retain the above copyright
7*    notice, this list of conditions and the following disclaimer.
8*  * Redistributions in binary form must reproduce the above
9*    copyright notice, this list of conditions and the following
10*    disclaimer in the documentation and/or other materials provided
11*    with the distribution.
12*  * Neither the name of The Linux Foundation nor the names of its
13*    contributors may be used to endorse or promote products derived
14*    from this software without specific prior written permission.
15*
16*
17* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29* Portions formerly licensed under Apache License, Version 2.0, are re licensed
30* under section 4 of Apache License, Version 2.0.
31
32* Copyright (C) 2010 The Android Open Source Project
33
34* Not a Contribution.
35
36* Licensed under the Apache License, Version 2.0 (the "License");
37* you may not use this file except in compliance with the License.
38* You may obtain a copy of the License at
39
40* http://www.apache.org/licenses/LICENSE-2.0
41
42* Unless required by applicable law or agreed to in writing, software
43* distributed under the License is distributed on an "AS IS" BASIS,
44* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
45* See the License for the specific language governing permissions and
46* limitations under the License.
47*/
48
49#include <hardware/hardware.h>
50#include <sync/sync.h>
51#include <copybit.h>
52#include <memalloc.h>
53#include <alloc_controller.h>
54#include <gr.h>
55
56#include <utils/constants.h>
57#include <utils/rect.h>
58#include <utils/formats.h>
59#include <algorithm>
60
61#include "blit_engine_c2d.h"
62#include "hwc_debugger.h"
63
64#define __CLASS__ "BlitEngineC2D"
65
66// TODO(user): Remove pragma after fixing sign conversion errors
67#if defined(__clang__)
68#pragma clang diagnostic push
69#pragma clang diagnostic ignored "-Wsign-conversion"
70#endif
71
72namespace sdm {
73
74
75BlitEngineC2d::RegionIterator::RegionIterator(LayerRectArray rect) {
76  rect_array = rect;
77  r.end = INT(rect.count);
78  r.current = 0;
79  this->next = iterate;
80}
81
82int BlitEngineC2d::RegionIterator::iterate(copybit_region_t const *self, copybit_rect_t *rect) {
83  if (!self || !rect) {
84    DLOGE("iterate invalid parameters");
85    return 0;
86  }
87
88  RegionIterator const *me = static_cast<RegionIterator const*>(self);
89  if (me->r.current != me->r.end) {
90    rect->l = INT(me->rect_array.rect[me->r.current].left);
91    rect->t = INT(me->rect_array.rect[me->r.current].top);
92    rect->r = INT(me->rect_array.rect[me->r.current].right);
93    rect->b = INT(me->rect_array.rect[me->r.current].bottom);
94    me->r.current++;
95    return 1;
96  }
97  return 0;
98}
99
100BlitEngineC2d::BlitEngineC2d() {
101  for (uint32_t i = 0; i < kNumBlitTargetBuffers; i++) {
102    blit_target_buffer_[i] = NULL;
103    release_fence_fd_[i] = -1;
104  }
105}
106
107BlitEngineC2d::~BlitEngineC2d() {
108  if (blit_engine_c2d_) {
109    copybit_close(blit_engine_c2d_);
110    blit_engine_c2d_ = NULL;
111  }
112  FreeBlitTargetBuffers();
113}
114
115int BlitEngineC2d::Init() {
116  hw_module_t const *module;
117  if (hw_get_module("copybit", &module) == 0) {
118    if (copybit_open(module, &blit_engine_c2d_) < 0) {
119      DLOGI("CopyBitC2D Open failed.");
120      return -1;
121    }
122    DLOGI("Opened Copybit Module");
123  } else {
124    DLOGI("Copybit HW Module not found");
125    return -1;
126  }
127
128  return 0;
129}
130
131void BlitEngineC2d::DeInit() {
132  FreeBlitTargetBuffers();
133  if (blit_engine_c2d_) {
134    copybit_close(blit_engine_c2d_);
135    blit_engine_c2d_ = NULL;
136  }
137}
138
139int BlitEngineC2d::AllocateBlitTargetBuffers(uint32_t width, uint32_t height, uint32_t format,
140                                             uint32_t usage) {
141  int status = 0;
142  if (width <= 0 || height <= 0) {
143    return false;
144  }
145
146  if (blit_target_buffer_[0]) {
147    // Free and reallocate the buffers if the w/h changes
148    if (INT(width) != blit_target_buffer_[0]->width ||
149        INT(height) != blit_target_buffer_[0]->height) {
150      FreeBlitTargetBuffers();
151    }
152  }
153
154  for (uint32_t i = 0; i < kNumBlitTargetBuffers; i++) {
155    if (blit_target_buffer_[i] == NULL) {
156      status = alloc_buffer(&blit_target_buffer_[i], width, height, format, usage);
157    }
158    if (status < 0) {
159      DLOGE("Allocation of Blit target Buffer failed");
160      FreeBlitTargetBuffers();
161      break;
162    }
163  }
164
165  return status;
166}
167
168void BlitEngineC2d::FreeBlitTargetBuffers() {
169  for (uint32_t i = 0; i < kNumBlitTargetBuffers; i++) {
170    private_handle_t **target_buffer = &blit_target_buffer_[i];
171    if (*target_buffer) {
172      // Free the valid fence
173      if (release_fence_fd_[i] >= 0) {
174        close(release_fence_fd_[i]);
175        release_fence_fd_[i] = -1;
176      }
177      free_buffer(*target_buffer);
178      *target_buffer = NULL;
179    }
180  }
181}
182
183int BlitEngineC2d::ClearTargetBuffer(private_handle_t* hnd, const LayerRect& rect) {
184  int status = 0;
185  copybit_rect_t clear_rect = {INT(rect.left), INT(rect.top), INT(rect.right), INT(rect.bottom)};
186
187  copybit_image_t buffer;
188  buffer.w = ALIGN((hnd->width), 32);
189  buffer.h = hnd->height;
190  buffer.format = hnd->format;
191  buffer.base = reinterpret_cast<void *>(hnd->base);
192  buffer.handle = reinterpret_cast<native_handle_t *>(hnd);
193  int dst_format_mode = COPYBIT_LINEAR;
194  if (hnd->flags & private_handle_t::PRIV_FLAGS_UBWC_ALIGNED) {
195    dst_format_mode = COPYBIT_UBWC_COMPRESSED;
196  }
197  blit_engine_c2d_->set_parameter(blit_engine_c2d_, COPYBIT_DST_FORMAT_MODE, dst_format_mode);
198
199  status = blit_engine_c2d_->clear(blit_engine_c2d_, &buffer, &clear_rect);
200  return status;
201}
202
203void BlitEngineC2d::PostCommit(LayerStack *layer_stack) {
204  int fence_fd = -1;
205  uint32_t count = 0;
206  int fd = -1;
207
208  for (uint32_t i = blit_target_start_index_-2; (i > 0) && (count < num_blit_target_); i--) {
209    Layer *layer = layer_stack->layers.at(i);
210    LayerBuffer *layer_buffer = layer->input_buffer;
211    if (layer->composition == kCompositionBlit) {
212      int index = blit_target_start_index_ + count;
213      layer_buffer->release_fence_fd =
214        layer_stack->layers.at(index)->input_buffer->release_fence_fd;
215      fence_fd = layer_buffer->release_fence_fd;
216      close(layer_buffer->acquire_fence_fd);
217      layer_buffer->acquire_fence_fd = -1;
218      layer_stack->layers.at(index)->input_buffer->release_fence_fd = -1;
219      fd = layer_stack->layers.at(index)->input_buffer->acquire_fence_fd;
220      layer_stack->layers.at(index)->input_buffer->acquire_fence_fd = -1;
221      count++;
222    }
223  }
224
225  if (fd >= 0) {
226    // Close the C2D fence FD
227    close(fd);
228  }
229  SetReleaseFence(fence_fd);
230}
231
232// Sync wait to close the previous fd
233void BlitEngineC2d::SetReleaseFence(int fd) {
234  if (release_fence_fd_[current_blit_target_index_] >= 0) {
235    int ret = -1;
236    ret = sync_wait(release_fence_fd_[current_blit_target_index_], 1000);
237    if (ret < 0) {
238      DLOGE("sync_wait error! errno = %d, err str = %s", errno, strerror(errno));
239    }
240    close(release_fence_fd_[current_blit_target_index_]);
241  }
242  release_fence_fd_[current_blit_target_index_] = dup(fd);
243}
244
245bool BlitEngineC2d::BlitActive() {
246  return blit_active_;
247}
248
249void BlitEngineC2d::SetFrameDumpConfig(uint32_t count) {
250  dump_frame_count_ = count;
251  dump_frame_index_ = 0;
252}
253
254int BlitEngineC2d::Prepare(LayerStack *layer_stack) {
255  blit_target_start_index_ = 0;
256
257  uint32_t layer_count = UINT32(layer_stack->layers.size());
258  uint32_t gpu_target_index = layer_count - 1;  // default assumption
259  uint32_t i = 0;
260
261  for (; i < layer_count; i++) {
262    Layer *layer = layer_stack->layers.at(i);
263
264    // No 10 bit support for C2D
265    if (Is10BitFormat(layer->input_buffer->format)) {
266      return -1;
267    }
268
269    if (layer->composition == kCompositionGPUTarget) {
270      // Need FBT size for allocating buffers
271      gpu_target_index = i;
272      break;
273    }
274  }
275
276  if ((layer_count - 1) == gpu_target_index) {
277    // No blit target layer
278    return -1;
279  }
280
281  blit_target_start_index_ = ++i;
282  num_blit_target_ = layer_count - blit_target_start_index_;
283
284  LayerBuffer *layer_buffer = layer_stack->layers.at(gpu_target_index)->input_buffer;
285  int fbwidth = INT(layer_buffer->width);
286  int fbheight = INT(layer_buffer->height);
287  if ((fbwidth < 0) || (fbheight < 0)) {
288    return -1;
289  }
290
291  current_blit_target_index_ = (current_blit_target_index_ + 1) % kNumBlitTargetBuffers;
292  int k = blit_target_start_index_;
293
294  for (uint32_t j = 0; j < num_blit_target_; j++, k++) {
295    Layer *layer = layer_stack->layers.at(k);
296    LayerBuffer *layer_buffer = layer->input_buffer;
297
298    // Set the buffer height and width
299    layer_buffer->width = fbwidth;
300    layer_buffer->height = fbheight/3;
301
302    layer->plane_alpha = 0xFF;
303    layer->blending = kBlendingOpaque;
304    layer->composition = kCompositionBlitTarget;
305    layer->frame_rate = layer_stack->layers.at(gpu_target_index)->frame_rate;
306  }
307
308  return 0;
309}
310
311int BlitEngineC2d::PreCommit(hwc_display_contents_1_t *content_list, LayerStack *layer_stack) {
312  int status = 0;
313  uint32_t num_app_layers = (uint32_t) content_list->numHwLayers-1;
314  int target_width = 0;
315  int target_height = 0;
316  uint32_t processed_blit = 0;
317  LayerRect dst_rects[kMaxBlitTargetLayers];
318  bool blit_needed = false;
319  uint32_t usage = 0;
320
321  if (!num_app_layers) {
322    return -1;
323  }
324
325  for (uint32_t i = num_app_layers-1; (i > 0) && (processed_blit < num_blit_target_); i--) {
326    Layer *layer = layer_stack->layers.at(i);
327    if (layer->composition != kCompositionBlit) {
328      continue;
329    }
330    blit_needed = true;
331    layer_stack->flags.attributes_changed = true;
332
333    Layer *blit_layer = layer_stack->layers.at(blit_target_start_index_ + processed_blit);
334    LayerRect &blit_src_rect = blit_layer->src_rect;
335    int width = INT(layer->dst_rect.right - layer->dst_rect.left);
336    int height = INT(layer->dst_rect.bottom - layer->dst_rect.top);
337    usage = GRALLOC_USAGE_PRIVATE_IOMMU_HEAP | GRALLOC_USAGE_HW_TEXTURE;
338    if (blit_engine_c2d_->get(blit_engine_c2d_, COPYBIT_UBWC_SUPPORT) > 0) {
339      usage |= GRALLOC_USAGE_PRIVATE_ALLOC_UBWC;
340    }
341    // TODO(user): FrameBuffer is assumed to be RGBA
342    AdrenoMemInfo::getInstance().getAlignedWidthAndHeight(width, height,
343                                 INT(HAL_PIXEL_FORMAT_RGBA_8888), usage, width, height);
344
345    target_width = std::max(target_width, width);
346    target_height += height;
347
348    // Left will be zero always
349    dst_rects[processed_blit].top = FLOAT(target_height - height);
350    dst_rects[processed_blit].right = dst_rects[processed_blit].left +
351                                      (layer->dst_rect.right - layer->dst_rect.left);
352    dst_rects[processed_blit].bottom = (dst_rects[processed_blit].top +
353                                      (layer->dst_rect.bottom - layer->dst_rect.top));
354    blit_src_rect = dst_rects[processed_blit];
355    processed_blit++;
356  }
357
358  // Allocate a single buffer of RGBA8888 format
359  if (blit_needed && (AllocateBlitTargetBuffers(target_width, target_height,
360                                                HAL_PIXEL_FORMAT_RGBA_8888, usage) < 0)) {
361      status = -1;
362      return status;
363  }
364
365  if (blit_needed) {
366    for (uint32_t j = 0; j < num_blit_target_; j++) {
367      Layer *layer = layer_stack->layers.at(j + content_list->numHwLayers);
368      private_handle_t *target_buffer = blit_target_buffer_[current_blit_target_index_];
369      // Set the fd information
370        layer->input_buffer->width = target_width;
371        layer->input_buffer->height = target_height;
372      if (target_buffer->flags & private_handle_t::PRIV_FLAGS_UBWC_ALIGNED) {
373          layer->input_buffer->format = kFormatRGBA8888Ubwc;
374      }
375      layer->input_buffer->planes[0].fd = target_buffer->fd;
376      layer->input_buffer->planes[0].offset = 0;
377      layer->input_buffer->planes[0].stride = target_buffer->width;
378    }
379  }
380
381  return status;
382}
383
384int BlitEngineC2d::Commit(hwc_display_contents_1_t *content_list, LayerStack *layer_stack) {
385  int fd = -1;
386  int status = 0;
387  bool hybrid_present = false;
388  uint32_t num_app_layers = (uint32_t) content_list->numHwLayers-1;
389  private_handle_t *target_buffer = blit_target_buffer_[current_blit_target_index_];
390  blit_active_ = false;
391
392  if (!num_app_layers) {
393    return -1;
394  }
395
396  // if not Blit Targets return
397  for (uint32_t i = 0; i < num_app_layers; i++) {
398    Layer *layer = layer_stack->layers.at(i);
399    if (layer->composition == kCompositionHybrid || layer->composition == kCompositionBlit) {
400      hybrid_present = true;
401    }
402  }
403
404  if (!hybrid_present) {
405    return status;
406  }
407
408  // Clear blit target buffer
409  LayerRect clear_rect;
410  clear_rect.left =  0;
411  clear_rect.top = 0;
412  clear_rect.right = FLOAT(target_buffer->width);
413  clear_rect.bottom = FLOAT(target_buffer->height);
414  ClearTargetBuffer(target_buffer, clear_rect);
415
416  int copybit_layer_count = 0;
417  uint32_t processed_blit = 0;
418  for (uint32_t i = num_app_layers-1; (i > 0) && (processed_blit < num_blit_target_) &&
419      (status == 0); i--) {
420    Layer *layer = layer_stack->layers.at(i);
421    if (layer->composition != kCompositionBlit) {
422      continue;
423    }
424
425    for (uint32_t k = 0; k <= i; k++) {
426      Layer *bottom_layer = layer_stack->layers.at(k);
427      LayerBuffer *layer_buffer = bottom_layer->input_buffer;
428      // if layer below the blit layer does not intersect, ignore that layer
429      LayerRect inter_sect = Intersection(layer->dst_rect, bottom_layer->dst_rect);
430      if (bottom_layer->composition != kCompositionHybrid && !IsValid(inter_sect)) {
431        continue;
432      }
433      if (bottom_layer->composition == kCompositionGPU ||
434          bottom_layer->composition == kCompositionSDE ||
435          bottom_layer->composition == kCompositionGPUTarget) {
436        continue;
437      }
438
439      // For each layer marked as Hybrid, wait for acquire fence and then blit using the C2D
440      if (layer_buffer->acquire_fence_fd >= 0) {
441        // Wait for acquire fence on the App buffers.
442        if (sync_wait(layer_buffer->acquire_fence_fd, 1000) < 0) {
443          DLOGE("sync_wait error!! error no = %d err str = %s", errno, strerror(errno));
444        }
445        layer_buffer->acquire_fence_fd = -1;
446      }
447      hwc_layer_1_t *hwc_layer = &content_list->hwLayers[k];
448      LayerRect &src_rect = bottom_layer->blit_regions.at(processed_blit);
449      Layer *blit_layer = layer_stack->layers.at(blit_target_start_index_ + processed_blit);
450      LayerRect dest_rect = blit_layer->src_rect;
451      int ret_val = DrawRectUsingCopybit(hwc_layer, bottom_layer, src_rect, dest_rect);
452      copybit_layer_count++;
453      if (ret_val < 0) {
454        copybit_layer_count = 0;
455        DLOGE("DrawRectUsingCopyBit failed");
456        status = -1;
457        break;
458      }
459    }
460    processed_blit++;
461  }
462
463  if (copybit_layer_count) {
464    blit_active_ = true;
465    blit_engine_c2d_->flush_get_fence(blit_engine_c2d_, &fd);
466  }
467
468  if (blit_active_) {
469    // dump the render buffer
470    DumpBlitTargetBuffer(fd);
471
472    // Set the fd to the LayerStack BlitTargets fd
473    uint32_t layer_count = UINT32(layer_stack->layers.size());
474    for (uint32_t k = blit_target_start_index_; k < layer_count; k++) {
475      Layer *layer = layer_stack->layers.at(k);
476      LayerBuffer *layer_buffer = layer->input_buffer;
477      layer_buffer->acquire_fence_fd = fd;
478    }
479  }
480
481  return status;
482}
483
484int BlitEngineC2d::DrawRectUsingCopybit(hwc_layer_1_t *hwc_layer, Layer *layer,
485                                        LayerRect blit_rect, LayerRect blit_dest_Rect) {
486  private_handle_t *target_buffer = blit_target_buffer_[current_blit_target_index_];
487  const private_handle_t *hnd = static_cast<const private_handle_t *>(hwc_layer->handle);
488  LayerBuffer *layer_buffer = layer->input_buffer;
489
490  // Set the Copybit Source
491  copybit_image_t src;
492  src.handle = const_cast<native_handle_t *>(hwc_layer->handle);
493  src.w = hnd->width;
494  src.h = hnd->height;
495  src.base = reinterpret_cast<void *>(hnd->base);
496  src.format = hnd->format;
497  src.horiz_padding = 0;
498  src.vert_padding = 0;
499
500  // Copybit source rect
501  copybit_rect_t src_rect = {INT(blit_rect.left), INT(blit_rect.top), INT(blit_rect.right),
502                            INT(blit_rect.bottom)};
503
504  // Copybit destination rect
505  copybit_rect_t dst_rect = {INT(blit_dest_Rect.left), INT(blit_dest_Rect.top),
506                            INT(blit_dest_Rect.right), INT(blit_dest_Rect.bottom)};
507
508  // Copybit destination buffer
509  copybit_image_t dst;
510  dst.handle = static_cast<native_handle_t *>(target_buffer);
511  dst.w = ALIGN(target_buffer->width, 32);
512  dst.h = ALIGN((target_buffer->height), 32);
513  dst.base = reinterpret_cast<void *>(target_buffer->base);
514  dst.format = target_buffer->format;
515
516  // Copybit region is the destRect
517  LayerRect region_rect;
518  region_rect.left = FLOAT(dst_rect.l);
519  region_rect.top = FLOAT(dst_rect.t);
520  region_rect.right = FLOAT(dst_rect.r);
521  region_rect.bottom = FLOAT(dst_rect.b);
522
523  LayerRectArray region;
524  region.count = 1;
525  region.rect  = &region_rect;
526  RegionIterator copybitRegion(region);
527  int acquireFd = layer_buffer->acquire_fence_fd;
528
529  // FRAMEBUFFER_WIDTH/HEIGHT for c2d is the target buffer w/h
530  blit_engine_c2d_->set_parameter(blit_engine_c2d_, COPYBIT_FRAMEBUFFER_WIDTH,
531                                  target_buffer->width);
532  blit_engine_c2d_->set_parameter(blit_engine_c2d_, COPYBIT_FRAMEBUFFER_HEIGHT,
533                                  target_buffer->height);
534  int transform = 0;
535  if (layer->transform.rotation != 0.0f) transform |= COPYBIT_TRANSFORM_ROT_90;
536  if (layer->transform.flip_horizontal) transform |= COPYBIT_TRANSFORM_FLIP_H;
537  if (layer->transform.flip_vertical) transform |= COPYBIT_TRANSFORM_FLIP_V;
538  blit_engine_c2d_->set_parameter(blit_engine_c2d_, COPYBIT_TRANSFORM, transform);
539  blit_engine_c2d_->set_parameter(blit_engine_c2d_, COPYBIT_PLANE_ALPHA, hwc_layer->planeAlpha);
540  blit_engine_c2d_->set_parameter(blit_engine_c2d_, COPYBIT_BLEND_MODE, hwc_layer->blending);
541  blit_engine_c2d_->set_parameter(blit_engine_c2d_, COPYBIT_DITHER,
542    (dst.format == HAL_PIXEL_FORMAT_RGB_565) ? COPYBIT_ENABLE : COPYBIT_DISABLE);
543
544  int src_format_mode = COPYBIT_LINEAR;
545  if (hnd->flags & private_handle_t::PRIV_FLAGS_UBWC_ALIGNED) {
546    src_format_mode = COPYBIT_UBWC_COMPRESSED;
547  }
548  blit_engine_c2d_->set_parameter(blit_engine_c2d_, COPYBIT_SRC_FORMAT_MODE, src_format_mode);
549
550  blit_engine_c2d_->set_sync(blit_engine_c2d_, acquireFd);
551  int err = blit_engine_c2d_->stretch(blit_engine_c2d_, &dst, &src, &dst_rect, &src_rect,
552                                      &copybitRegion);
553
554  if (err < 0) {
555    DLOGE("copybit stretch failed");
556  }
557
558  return err;
559}
560
561void BlitEngineC2d::DumpBlitTargetBuffer(int fd) {
562  if (!dump_frame_count_) {
563    return;
564  }
565
566  private_handle_t *target_buffer = blit_target_buffer_[current_blit_target_index_];
567
568  if (fd >= 0) {
569    int error = sync_wait(fd, 1000);
570    if (error < 0) {
571      DLOGW("sync_wait error errno = %d, desc = %s", errno, strerror(errno));
572      return;
573    }
574  }
575
576  char dump_file_name[PATH_MAX];
577  size_t result = 0;
578  snprintf(dump_file_name, sizeof(dump_file_name), "/data/misc/display/frame_dump_primary"
579           "/blit_target_%d.raw", (dump_frame_index_));
580  FILE* fp = fopen(dump_file_name, "w+");
581  if (fp) {
582    result = fwrite(reinterpret_cast<void *>(target_buffer->base), target_buffer->size, 1, fp);
583    fclose(fp);
584  }
585  dump_frame_count_--;
586  dump_frame_index_++;
587}
588
589}  // namespace sdm
590#if defined(__clang__)
591#pragma clang diagnostic pop
592#endif
593
594