You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
341 lines
12 KiB
341 lines
12 KiB
/* |
|
* Copyright (c) 2016 - 2017, 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. |
|
*/ |
|
|
|
#include <alloc_controller.h> |
|
#include <gr.h> |
|
#include <gralloc_priv.h> |
|
#include <memalloc.h> |
|
#include <sync/sync.h> |
|
|
|
#include <TonemapFactory.h> |
|
|
|
#include <core/buffer_allocator.h> |
|
|
|
#include <utils/constants.h> |
|
#include <utils/debug.h> |
|
#include <utils/formats.h> |
|
#include <utils/rect.h> |
|
#include <utils/utils.h> |
|
|
|
#include <vector> |
|
|
|
#include "hwc_debugger.h" |
|
#include "hwc_tonemapper.h" |
|
|
|
#define __CLASS__ "HWCToneMapper" |
|
|
|
namespace sdm { |
|
|
|
ToneMapSession::~ToneMapSession() { |
|
delete gpu_tone_mapper_; |
|
gpu_tone_mapper_ = NULL; |
|
FreeIntermediateBuffers(); |
|
} |
|
|
|
DisplayError ToneMapSession::AllocateIntermediateBuffers(int w, int h, int format, int usage) { |
|
for (uint8_t i = 0; i < kNumIntermediateBuffers; i++) { |
|
int status = alloc_buffer(&intermediate_buffer_[i], w, h, format, usage); |
|
if (status < 0) { |
|
FreeIntermediateBuffers(); |
|
return kErrorMemory; |
|
} |
|
} |
|
|
|
return kErrorNone; |
|
} |
|
|
|
void ToneMapSession::FreeIntermediateBuffers() { |
|
for (uint8_t i = 0; i < kNumIntermediateBuffers; i++) { |
|
private_handle_t *buffer = intermediate_buffer_[i]; |
|
if (buffer) { |
|
// Free the valid fence |
|
if (release_fence_fd_[i] >= 0) { |
|
CloseFd(&release_fence_fd_[i]); |
|
} |
|
free_buffer(buffer); |
|
intermediate_buffer_[i] = NULL; |
|
} |
|
} |
|
} |
|
|
|
void ToneMapSession::UpdateBuffer(int acquire_fence, LayerBuffer *buffer) { |
|
// Acquire fence will be closed by HWC Display. |
|
// Fence returned by GPU will be closed in PostCommit. |
|
buffer->acquire_fence_fd = acquire_fence; |
|
buffer->size = intermediate_buffer_[current_buffer_index_]->size; |
|
buffer->planes[0].fd = intermediate_buffer_[current_buffer_index_]->fd; |
|
} |
|
|
|
void ToneMapSession::SetReleaseFence(int fd) { |
|
CloseFd(&release_fence_fd_[current_buffer_index_]); |
|
// Used to give to GPU tonemapper along with input layer fd |
|
release_fence_fd_[current_buffer_index_] = dup(fd); |
|
} |
|
|
|
void ToneMapSession::SetToneMapConfig(Layer *layer) { |
|
// HDR -> SDR is FORWARD and SDR - > HDR is INVERSE |
|
tone_map_config_.type = layer->input_buffer.flags.hdr ? TONEMAP_FORWARD : TONEMAP_INVERSE; |
|
tone_map_config_.colorPrimaries = layer->input_buffer.color_metadata.colorPrimaries; |
|
tone_map_config_.transfer = layer->input_buffer.color_metadata.transfer; |
|
tone_map_config_.secure = layer->request.flags.secure; |
|
tone_map_config_.format = layer->request.format; |
|
} |
|
|
|
bool ToneMapSession::IsSameToneMapConfig(Layer *layer) { |
|
LayerBuffer& buffer = layer->input_buffer; |
|
private_handle_t *handle = intermediate_buffer_[0]; |
|
int tonemap_type = buffer.flags.hdr ? TONEMAP_FORWARD : TONEMAP_INVERSE; |
|
|
|
return ((tonemap_type == tone_map_config_.type) && |
|
(buffer.color_metadata.colorPrimaries == tone_map_config_.colorPrimaries) && |
|
(buffer.color_metadata.transfer == tone_map_config_.transfer) && |
|
(layer->request.flags.secure == tone_map_config_.secure) && |
|
(layer->request.format == tone_map_config_.format) && |
|
(layer->request.width == UINT32(handle->unaligned_width)) && |
|
(layer->request.height == UINT32(handle->unaligned_height))); |
|
} |
|
|
|
int HWCToneMapper::HandleToneMap(hwc_display_contents_1_t *content_list, LayerStack *layer_stack) { |
|
uint32_t gpu_count = 0; |
|
DisplayError error = kErrorNone; |
|
|
|
for (uint32_t i = 0; i < layer_stack->layers.size(); i++) { |
|
uint32_t session_index = 0; |
|
Layer *layer = layer_stack->layers.at(i); |
|
if (layer->composition == kCompositionGPU) { |
|
gpu_count++; |
|
} |
|
|
|
if (layer->request.flags.tone_map) { |
|
switch (layer->composition) { |
|
case kCompositionGPUTarget: |
|
if (!gpu_count) { |
|
// When all layers are on FrameBuffer and if they do not update in the next draw cycle, |
|
// then SDM marks them for SDE Composition because the cached FB layer gets displayed. |
|
// GPU count will be 0 in this case. Try to use the existing tone-mapped frame buffer. |
|
// No ToneMap/Blit is required. Just update the buffer & acquire fence fd of FB layer. |
|
if (!tone_map_sessions_.empty()) { |
|
ToneMapSession *fb_tone_map_session = tone_map_sessions_.at(fb_session_index_); |
|
fb_tone_map_session->UpdateBuffer(-1 /* acquire_fence */, &layer->input_buffer); |
|
fb_tone_map_session->layer_index_ = INT(i); |
|
fb_tone_map_session->acquired_ = true; |
|
return 0; |
|
} |
|
} |
|
error = AcquireToneMapSession(layer, &session_index); |
|
fb_session_index_ = session_index; |
|
break; |
|
default: |
|
error = AcquireToneMapSession(layer, &session_index); |
|
break; |
|
} |
|
|
|
if (error != kErrorNone) { |
|
Terminate(); |
|
return -1; |
|
} |
|
|
|
ToneMapSession *session = tone_map_sessions_.at(session_index); |
|
ToneMap(&content_list->hwLayers[i], layer, session); |
|
session->layer_index_ = INT(i); |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
void HWCToneMapper::ToneMap(hwc_layer_1_t *hwc_layer, Layer* layer, ToneMapSession *session) { |
|
int fence_fd = -1; |
|
int acquire_fd = -1; |
|
int merged_fd = -1; |
|
|
|
uint8_t buffer_index = session->current_buffer_index_; |
|
const private_handle_t *dst_hnd = session->intermediate_buffer_[buffer_index]; |
|
const private_handle_t *src_hnd = static_cast<const private_handle_t *>(hwc_layer->handle); |
|
|
|
acquire_fd = dup(layer->input_buffer.acquire_fence_fd); |
|
buffer_sync_handler_.SyncMerge(session->release_fence_fd_[buffer_index], acquire_fd, &merged_fd); |
|
|
|
if (acquire_fd >= 0) { |
|
CloseFd(&acquire_fd); |
|
} |
|
|
|
if (session->release_fence_fd_[buffer_index] >= 0) { |
|
CloseFd(&session->release_fence_fd_[buffer_index]); |
|
} |
|
|
|
DTRACE_BEGIN("GPU_TM_BLIT"); |
|
fence_fd = session->gpu_tone_mapper_->blit(reinterpret_cast<const void *>(dst_hnd), |
|
reinterpret_cast<const void *>(src_hnd), merged_fd); |
|
DTRACE_END(); |
|
|
|
DumpToneMapOutput(session, &fence_fd); |
|
session->UpdateBuffer(fence_fd, &layer->input_buffer); |
|
} |
|
|
|
void HWCToneMapper::PostCommit(LayerStack *layer_stack) { |
|
auto it = tone_map_sessions_.begin(); |
|
while (it != tone_map_sessions_.end()) { |
|
uint32_t session_index = UINT32(std::distance(tone_map_sessions_.begin(), it)); |
|
ToneMapSession *session = tone_map_sessions_.at(session_index); |
|
Layer *layer = layer_stack->layers.at(UINT32(session->layer_index_)); |
|
if (session->acquired_) { |
|
// Close the fd returned by GPU ToneMapper and set release fence. |
|
LayerBuffer &layer_buffer = layer->input_buffer; |
|
CloseFd(&layer_buffer.acquire_fence_fd); |
|
session->SetReleaseFence(layer_buffer.release_fence_fd); |
|
session->acquired_ = false; |
|
it++; |
|
} else { |
|
delete session; |
|
it = tone_map_sessions_.erase(it); |
|
} |
|
} |
|
} |
|
|
|
void HWCToneMapper::Terminate() { |
|
if (tone_map_sessions_.size()) { |
|
while (!tone_map_sessions_.empty()) { |
|
delete tone_map_sessions_.back(); |
|
tone_map_sessions_.pop_back(); |
|
} |
|
fb_session_index_ = 0; |
|
} |
|
} |
|
|
|
void HWCToneMapper::SetFrameDumpConfig(uint32_t count) { |
|
DLOGI("Dump FrameConfig count = %d", count); |
|
dump_frame_count_ = count; |
|
dump_frame_index_ = 0; |
|
} |
|
|
|
void HWCToneMapper::DumpToneMapOutput(ToneMapSession *session, int *acquire_fd) { |
|
if (!dump_frame_count_) { |
|
return; |
|
} |
|
|
|
private_handle_t *target_buffer = session->intermediate_buffer_[session->current_buffer_index_]; |
|
|
|
if (*acquire_fd >= 0) { |
|
int error = sync_wait(*acquire_fd, 1000); |
|
if (error < 0) { |
|
DLOGW("sync_wait error errno = %d, desc = %s", errno, strerror(errno)); |
|
return; |
|
} |
|
} |
|
|
|
size_t result = 0; |
|
char dump_file_name[PATH_MAX]; |
|
snprintf(dump_file_name, sizeof(dump_file_name), "/data/misc/display/frame_dump_primary" |
|
"/tonemap_%dx%d_frame%d.raw", target_buffer->width, target_buffer->height, |
|
dump_frame_index_); |
|
|
|
FILE* fp = fopen(dump_file_name, "w+"); |
|
if (fp) { |
|
DLOGI("base addr = %x", target_buffer->base); |
|
result = fwrite(reinterpret_cast<void *>(target_buffer->base), target_buffer->size, 1, fp); |
|
fclose(fp); |
|
} |
|
dump_frame_count_--; |
|
dump_frame_index_++; |
|
CloseFd(acquire_fd); |
|
} |
|
|
|
DisplayError HWCToneMapper::AcquireToneMapSession(Layer *layer, uint32_t *session_index) { |
|
Color10Bit *grid_entries = NULL; |
|
int grid_size = 0; |
|
|
|
if (layer->lut_3d.validGridEntries) { |
|
grid_entries = layer->lut_3d.gridEntries; |
|
grid_size = INT(layer->lut_3d.gridSize); |
|
} |
|
|
|
// When the property sdm.disable_hdr_lut_gen is set, the lutEntries and gridEntries in |
|
// the Lut3d will be NULL, clients needs to allocate the memory and set correct 3D Lut |
|
// for Tonemapping. |
|
if (!layer->lut_3d.lutEntries || !layer->lut_3d.dim) { |
|
// Atleast lutEntries must be valid for GPU Tonemapper. |
|
DLOGE("Invalid Lut Entries or lut dimension = %d", layer->lut_3d.dim); |
|
return kErrorParameters; |
|
} |
|
|
|
// Check if we can re-use an existing tone map session. |
|
for (uint32_t i = 0; i < tone_map_sessions_.size(); i++) { |
|
ToneMapSession *tonemap_session = tone_map_sessions_.at(i); |
|
if (!tonemap_session->acquired_ && tonemap_session->IsSameToneMapConfig(layer)) { |
|
tonemap_session->current_buffer_index_ = (tonemap_session->current_buffer_index_ + 1) % |
|
ToneMapSession::kNumIntermediateBuffers; |
|
tonemap_session->acquired_ = true; |
|
*session_index = i; |
|
return kErrorNone; |
|
} |
|
} |
|
|
|
ToneMapSession *session = new ToneMapSession(); |
|
|
|
session->SetToneMapConfig(layer); |
|
session->gpu_tone_mapper_ = TonemapperFactory_GetInstance(session->tone_map_config_.type, |
|
layer->lut_3d.lutEntries, |
|
layer->lut_3d.dim, |
|
grid_entries, grid_size, |
|
session->tone_map_config_.secure); |
|
|
|
if (session->gpu_tone_mapper_ == NULL) { |
|
DLOGE("Get Tonemapper failed!"); |
|
delete session; |
|
return kErrorNotSupported; |
|
} |
|
|
|
int status, format; |
|
DisplayError error = kErrorNone; |
|
int usage = INT(GRALLOC_USAGE_PRIVATE_IOMMU_HEAP | GRALLOC_USAGE_HW_TEXTURE); |
|
|
|
if (layer->request.flags.secure) { |
|
usage = INT(GRALLOC_USAGE_PRIVATE_MM_HEAP); |
|
usage |= INT(GRALLOC_USAGE_PROTECTED); |
|
} |
|
|
|
status = buffer_allocator_.SetBufferInfo(layer->request.format, &format, &usage); |
|
error = session->AllocateIntermediateBuffers(INT(layer->request.width), |
|
INT(layer->request.height), format, usage); |
|
|
|
if (error != kErrorNone) { |
|
DLOGE("Allocation of Intermediate Buffers failed!"); |
|
delete session; |
|
return error; |
|
} |
|
|
|
session->acquired_ = true; |
|
tone_map_sessions_.push_back(session); |
|
*session_index = UINT32(tone_map_sessions_.size() - 1); |
|
|
|
return kErrorNone; |
|
} |
|
|
|
} // namespace sdm
|
|
|