/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of The Linux Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * Portions formerly licensed under Apache License, Version 2.0, are re licensed * under section 4 of Apache License, Version 2.0. * Copyright (C) 2010 The Android Open Source Project * Not a Contribution. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include "blit_engine_c2d.h" #include "hwc_debugger.h" #define __CLASS__ "BlitEngineC2D" // TODO(user): Remove pragma after fixing sign conversion errors #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wsign-conversion" #endif namespace sdm { BlitEngineC2d::RegionIterator::RegionIterator(LayerRectArray rect) { rect_array = rect; r.end = INT(rect.count); r.current = 0; this->next = iterate; } int BlitEngineC2d::RegionIterator::iterate(copybit_region_t const *self, copybit_rect_t *rect) { if (!self || !rect) { DLOGE("iterate invalid parameters"); return 0; } RegionIterator const *me = static_cast(self); if (me->r.current != me->r.end) { rect->l = INT(me->rect_array.rect[me->r.current].left); rect->t = INT(me->rect_array.rect[me->r.current].top); rect->r = INT(me->rect_array.rect[me->r.current].right); rect->b = INT(me->rect_array.rect[me->r.current].bottom); me->r.current++; return 1; } return 0; } BlitEngineC2d::BlitEngineC2d() { for (uint32_t i = 0; i < kNumBlitTargetBuffers; i++) { blit_target_buffer_[i] = NULL; release_fence_fd_[i] = -1; } HWCDebugHandler::Get()->GetProperty("persist.hwc.blit.comp", &blit_supported_); } BlitEngineC2d::~BlitEngineC2d() { if (blit_engine_c2d_) { copybit_close(blit_engine_c2d_); blit_engine_c2d_ = NULL; } FreeBlitTargetBuffers(); } int BlitEngineC2d::Init() { if (!blit_supported_) { return -1; } hw_module_t const *module; if (hw_get_module("copybit", &module) == 0) { if (copybit_open(module, &blit_engine_c2d_) < 0) { DLOGI("CopyBitC2D Open failed."); return -1; } DLOGI("Opened Copybit Module"); } else { DLOGI("Copybit HW Module not found"); return -1; } return 0; } void BlitEngineC2d::DeInit() { FreeBlitTargetBuffers(); if (blit_engine_c2d_) { copybit_close(blit_engine_c2d_); blit_engine_c2d_ = NULL; } } int BlitEngineC2d::AllocateBlitTargetBuffers(uint32_t width, uint32_t height, uint32_t format, uint32_t usage) { int status = 0; if (width <= 0 || height <= 0) { return false; } if (blit_target_buffer_[0]) { // Free and reallocate the buffers if the w/h changes if (INT(width) != blit_target_buffer_[0]->width || INT(height) != blit_target_buffer_[0]->height) { FreeBlitTargetBuffers(); } } for (uint32_t i = 0; i < kNumBlitTargetBuffers; i++) { if (blit_target_buffer_[i] == NULL) { status = alloc_buffer(&blit_target_buffer_[i], width, height, format, usage); } if (status < 0) { DLOGE("Allocation of Blit target Buffer failed"); FreeBlitTargetBuffers(); break; } } return status; } void BlitEngineC2d::FreeBlitTargetBuffers() { for (uint32_t i = 0; i < kNumBlitTargetBuffers; i++) { private_handle_t **target_buffer = &blit_target_buffer_[i]; if (*target_buffer) { // Free the valid fence if (release_fence_fd_[i] >= 0) { close(release_fence_fd_[i]); release_fence_fd_[i] = -1; } free_buffer(*target_buffer); *target_buffer = NULL; } } } int BlitEngineC2d::ClearTargetBuffer(private_handle_t* hnd, const LayerRect& rect) { int status = 0; copybit_rect_t clear_rect = {INT(rect.left), INT(rect.top), INT(rect.right), INT(rect.bottom)}; copybit_image_t buffer; buffer.w = ALIGN((hnd->width), 32); buffer.h = hnd->height; buffer.format = hnd->format; buffer.base = reinterpret_cast(hnd->base); buffer.handle = reinterpret_cast(hnd); int dst_format_mode = COPYBIT_LINEAR; if (hnd->flags & private_handle_t::PRIV_FLAGS_UBWC_ALIGNED) { dst_format_mode = COPYBIT_UBWC_COMPRESSED; } blit_engine_c2d_->set_parameter(blit_engine_c2d_, COPYBIT_DST_FORMAT_MODE, dst_format_mode); status = blit_engine_c2d_->clear(blit_engine_c2d_, &buffer, &clear_rect); return status; } void BlitEngineC2d::PostCommit(LayerStack *layer_stack) { int fence_fd = -1; uint32_t count = 0; int fd = -1; for (uint32_t i = blit_target_start_index_-2; (i > 0) && (count < num_blit_target_); i--) { Layer &layer = layer_stack->layers[i]; LayerBuffer *layer_buffer = layer.input_buffer; if (layer.composition == kCompositionBlit) { int index = blit_target_start_index_ + count; layer_buffer->release_fence_fd = layer_stack->layers[index].input_buffer->release_fence_fd; fence_fd = layer_buffer->release_fence_fd; close(layer_buffer->acquire_fence_fd); layer_buffer->acquire_fence_fd = -1; layer_stack->layers[index].input_buffer->release_fence_fd = -1; fd = layer_stack->layers[index].input_buffer->acquire_fence_fd; layer_stack->layers[index].input_buffer->acquire_fence_fd = -1; count++; } } if (fd >= 0) { // Close the C2D fence FD close(fd); } SetReleaseFence(fence_fd); } // Sync wait to close the previous fd void BlitEngineC2d::SetReleaseFence(int fd) { if (release_fence_fd_[current_blit_target_index_] >= 0) { int ret = -1; ret = sync_wait(release_fence_fd_[current_blit_target_index_], 1000); if (ret < 0) { DLOGE("sync_wait error! errno = %d, err str = %s", errno, strerror(errno)); } close(release_fence_fd_[current_blit_target_index_]); } release_fence_fd_[current_blit_target_index_] = dup(fd); } bool BlitEngineC2d::BlitActive() { return blit_active_; } void BlitEngineC2d::SetFrameDumpConfig(uint32_t count) { dump_frame_count_ = count; dump_frame_index_ = 0; } int BlitEngineC2d::Prepare(LayerStack *layer_stack) { blit_target_start_index_ = 0; uint32_t gpu_target_index = layer_stack->layer_count-1; uint32_t i = INT(layer_stack->layer_count-1); for (i = 0; i < layer_stack->layer_count; i++) { Layer &layer = layer_stack->layers[i]; if (!blit_supported_) { return -1; } // No 10 bit support for C2D if (Is10BitFormat(layer.input_buffer->format)) { return -1; } if (layer.composition == kCompositionGPUTarget) { // Need FBT size for allocating buffers gpu_target_index = i; break; } } if ((layer_stack->layer_count-1) == gpu_target_index) { // No blit target layer return -1; } blit_target_start_index_ = ++i; num_blit_target_ = layer_stack->layer_count - blit_target_start_index_; LayerBuffer *layer_buffer = layer_stack->layers[gpu_target_index].input_buffer; int fbwidth = INT(layer_buffer->width); int fbheight = INT(layer_buffer->height); if ((fbwidth < 0) || (fbheight < 0)) { return -1; } current_blit_target_index_ = (current_blit_target_index_ + 1) % kNumBlitTargetBuffers; int k = blit_target_start_index_; for (uint32_t j = 0; j < num_blit_target_; j++, k++) { Layer &layer = layer_stack->layers[k]; LayerBuffer *layer_buffer = layer.input_buffer; // Set the buffer height and width layer_buffer->width = fbwidth; layer_buffer->height = fbheight/3; layer.plane_alpha = 0xFF; layer.blending = kBlendingOpaque; layer.composition = kCompositionBlitTarget; layer.frame_rate = layer_stack->layers[gpu_target_index].frame_rate; } return 0; } int BlitEngineC2d::PreCommit(hwc_display_contents_1_t *content_list, LayerStack *layer_stack) { int status = 0; uint32_t num_app_layers = (uint32_t) content_list->numHwLayers-1; int target_width = 0; int target_height = 0; uint32_t processed_blit = 0; LayerRect dst_rects[kMaxBlitTargetLayers]; bool blit_needed = false; uint32_t usage = 0; if (!num_app_layers) { return -1; } for (uint32_t i = num_app_layers-1; (i > 0) && (processed_blit < num_blit_target_); i--) { Layer &layer = layer_stack->layers[i]; if (layer.composition != kCompositionBlit) { continue; } blit_needed = true; layer_stack->flags.attributes_changed = true; Layer &blit_layer = layer_stack->layers[blit_target_start_index_ + processed_blit]; LayerRect &blit_src_rect = blit_layer.src_rect; int width = INT(layer.dst_rect.right - layer.dst_rect.left); int height = INT(layer.dst_rect.bottom - layer.dst_rect.top); usage = GRALLOC_USAGE_PRIVATE_IOMMU_HEAP | GRALLOC_USAGE_HW_TEXTURE; if (blit_engine_c2d_->get(blit_engine_c2d_, COPYBIT_UBWC_SUPPORT) > 0) { usage |= GRALLOC_USAGE_PRIVATE_ALLOC_UBWC; } // TODO(user): FrameBuffer is assumed to be RGBA AdrenoMemInfo::getInstance().getAlignedWidthAndHeight(width, height, INT(HAL_PIXEL_FORMAT_RGBA_8888), usage, width, height); target_width = MAX(target_width, width); target_height += height; // Left will be zero always dst_rects[processed_blit].top = FLOAT(target_height - height); dst_rects[processed_blit].right = dst_rects[processed_blit].left + (layer.dst_rect.right - layer.dst_rect.left); dst_rects[processed_blit].bottom = (dst_rects[processed_blit].top + (layer.dst_rect.bottom - layer.dst_rect.top)); blit_src_rect = dst_rects[processed_blit]; processed_blit++; } // Allocate a single buffer of RGBA8888 format if (blit_needed && (AllocateBlitTargetBuffers(target_width, target_height, HAL_PIXEL_FORMAT_RGBA_8888, usage) < 0)) { status = -1; return status; } if (blit_needed) { for (uint32_t j = 0; j < num_blit_target_; j++) { Layer &layer = layer_stack->layers[j + content_list->numHwLayers]; private_handle_t *target_buffer = blit_target_buffer_[current_blit_target_index_]; // Set the fd information if (layer.input_buffer) { layer.input_buffer->width = target_width; layer.input_buffer->height = target_height; if (target_buffer->flags & private_handle_t::PRIV_FLAGS_UBWC_ALIGNED) { layer.input_buffer->format = kFormatRGBA8888Ubwc; } layer.input_buffer->planes[0].fd = target_buffer->fd; layer.input_buffer->planes[0].offset = 0; layer.input_buffer->planes[0].stride = target_buffer->width; } } } return status; } int BlitEngineC2d::Commit(hwc_display_contents_1_t *content_list, LayerStack *layer_stack) { int fd = -1; int status = 0; bool hybrid_present = false; uint32_t num_app_layers = (uint32_t) content_list->numHwLayers-1; private_handle_t *target_buffer = blit_target_buffer_[current_blit_target_index_]; blit_active_ = false; if (!num_app_layers) { return -1; } // if not Blit Targets return for (uint32_t i = 0; i < num_app_layers; i++) { Layer &layer = layer_stack->layers[i]; if (layer.composition == kCompositionHybrid || layer.composition == kCompositionBlit) { hybrid_present = true; } } if (!hybrid_present) { return status; } // Clear blit target buffer LayerRect clear_rect; clear_rect.left = 0; clear_rect.top = 0; clear_rect.right = FLOAT(target_buffer->width); clear_rect.bottom = FLOAT(target_buffer->height); ClearTargetBuffer(target_buffer, clear_rect); int copybit_layer_count = 0; uint32_t processed_blit = 0; for (uint32_t i = num_app_layers-1; (i > 0) && (processed_blit < num_blit_target_) && (status == 0); i--) { Layer &layer = layer_stack->layers[i]; if (layer.composition != kCompositionBlit) { continue; } for (uint32_t k = 0; k <= i; k++) { Layer &bottom_layer = layer_stack->layers[k]; LayerBuffer *layer_buffer = bottom_layer.input_buffer; // if layer below the blit layer does not intersect, ignore that layer LayerRect inter_sect = Intersection(layer.dst_rect, bottom_layer.dst_rect); if (bottom_layer.composition != kCompositionHybrid && !IsValid(inter_sect)) { continue; } if (bottom_layer.composition == kCompositionGPU || bottom_layer.composition == kCompositionSDE || bottom_layer.composition == kCompositionGPUTarget) { continue; } // For each layer marked as Hybrid, wait for acquire fence and then blit using the C2D if (layer_buffer->acquire_fence_fd >= 0) { // Wait for acquire fence on the App buffers. if (sync_wait(layer_buffer->acquire_fence_fd, 1000) < 0) { DLOGE("sync_wait error!! error no = %d err str = %s", errno, strerror(errno)); } layer_buffer->acquire_fence_fd = -1; } hwc_layer_1_t *hwc_layer = &content_list->hwLayers[k]; LayerRect src_rect = bottom_layer.blit_regions.rect[processed_blit]; Layer &blit_layer = layer_stack->layers[blit_target_start_index_ + processed_blit]; LayerRect dest_rect = blit_layer.src_rect; int ret_val = DrawRectUsingCopybit(hwc_layer, &bottom_layer, src_rect, dest_rect); copybit_layer_count++; if (ret_val < 0) { copybit_layer_count = 0; DLOGE("DrawRectUsingCopyBit failed"); status = -1; break; } } processed_blit++; } if (copybit_layer_count) { blit_active_ = true; blit_engine_c2d_->flush_get_fence(blit_engine_c2d_, &fd); } if (blit_active_) { // dump the render buffer DumpBlitTargetBuffer(fd); // Set the fd to the LayerStack BlitTargets fd for (uint32_t k = blit_target_start_index_; k < layer_stack->layer_count; k++) { Layer &layer = layer_stack->layers[k]; LayerBuffer *layer_buffer = layer.input_buffer; layer_buffer->acquire_fence_fd = fd; } } return status; } int BlitEngineC2d::DrawRectUsingCopybit(hwc_layer_1_t *hwc_layer, Layer *layer, LayerRect blit_rect, LayerRect blit_dest_Rect) { private_handle_t *target_buffer = blit_target_buffer_[current_blit_target_index_]; const private_handle_t *hnd = static_cast(hwc_layer->handle); LayerBuffer *layer_buffer = layer->input_buffer; // Set the Copybit Source copybit_image_t src; src.handle = const_cast(hwc_layer->handle); src.w = hnd->width; src.h = hnd->height; src.base = reinterpret_cast(hnd->base); src.format = hnd->format; src.horiz_padding = 0; src.vert_padding = 0; // Copybit source rect copybit_rect_t src_rect = {INT(blit_rect.left), INT(blit_rect.top), INT(blit_rect.right), INT(blit_rect.bottom)}; // Copybit destination rect copybit_rect_t dst_rect = {INT(blit_dest_Rect.left), INT(blit_dest_Rect.top), INT(blit_dest_Rect.right), INT(blit_dest_Rect.bottom)}; // Copybit destination buffer copybit_image_t dst; dst.handle = static_cast(target_buffer); dst.w = ALIGN(target_buffer->width, 32); dst.h = ALIGN((target_buffer->height), 32); dst.base = reinterpret_cast(target_buffer->base); dst.format = target_buffer->format; // Copybit region is the destRect LayerRect region_rect; region_rect.left = FLOAT(dst_rect.l); region_rect.top = FLOAT(dst_rect.t); region_rect.right = FLOAT(dst_rect.r); region_rect.bottom = FLOAT(dst_rect.b); LayerRectArray region; region.count = 1; region.rect = ®ion_rect; RegionIterator copybitRegion(region); int acquireFd = layer_buffer->acquire_fence_fd; // FRAMEBUFFER_WIDTH/HEIGHT for c2d is the target buffer w/h blit_engine_c2d_->set_parameter(blit_engine_c2d_, COPYBIT_FRAMEBUFFER_WIDTH, target_buffer->width); blit_engine_c2d_->set_parameter(blit_engine_c2d_, COPYBIT_FRAMEBUFFER_HEIGHT, target_buffer->height); int transform = 0; if (layer->transform.rotation != 0.0f) transform |= COPYBIT_TRANSFORM_ROT_90; if (layer->transform.flip_horizontal) transform |= COPYBIT_TRANSFORM_FLIP_H; if (layer->transform.flip_vertical) transform |= COPYBIT_TRANSFORM_FLIP_V; blit_engine_c2d_->set_parameter(blit_engine_c2d_, COPYBIT_TRANSFORM, transform); blit_engine_c2d_->set_parameter(blit_engine_c2d_, COPYBIT_PLANE_ALPHA, hwc_layer->planeAlpha); blit_engine_c2d_->set_parameter(blit_engine_c2d_, COPYBIT_BLEND_MODE, hwc_layer->blending); blit_engine_c2d_->set_parameter(blit_engine_c2d_, COPYBIT_DITHER, (dst.format == HAL_PIXEL_FORMAT_RGB_565) ? COPYBIT_ENABLE : COPYBIT_DISABLE); int src_format_mode = COPYBIT_LINEAR; if (hnd->flags & private_handle_t::PRIV_FLAGS_UBWC_ALIGNED) { src_format_mode = COPYBIT_UBWC_COMPRESSED; } blit_engine_c2d_->set_parameter(blit_engine_c2d_, COPYBIT_SRC_FORMAT_MODE, src_format_mode); blit_engine_c2d_->set_sync(blit_engine_c2d_, acquireFd); int err = blit_engine_c2d_->stretch(blit_engine_c2d_, &dst, &src, &dst_rect, &src_rect, ©bitRegion); if (err < 0) { DLOGE("copybit stretch failed"); } return err; } void BlitEngineC2d::DumpBlitTargetBuffer(int fd) { if (!dump_frame_count_) { return; } private_handle_t *target_buffer = blit_target_buffer_[current_blit_target_index_]; if (fd >= 0) { int error = sync_wait(fd, 1000); if (error < 0) { DLOGW("sync_wait error errno = %d, desc = %s", errno, strerror(errno)); return; } } char dump_file_name[PATH_MAX]; size_t result = 0; snprintf(dump_file_name, sizeof(dump_file_name), "/data/misc/display/frame_dump_primary" "/blit_target_%d.raw", (dump_frame_index_)); FILE* fp = fopen(dump_file_name, "w+"); if (fp) { result = fwrite(reinterpret_cast(target_buffer->base), target_buffer->size, 1, fp); fclose(fp); } dump_frame_count_--; dump_frame_index_++; } } // namespace sdm #if defined(__clang__) #pragma clang diagnostic pop #endif