blob: 6e2700f7c1e3872cd90c402bdc68860631f81afb [file] [log] [blame]
/*
* Copyright (c) 2019-2021, 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 "hwc_display_virtual_gpu.h"
#include "hwc_session.h"
#include "QtiGralloc.h"
#define __CLASS__ "HWCDisplayVirtualGPU"
namespace sdm {
int HWCDisplayVirtualGPU::Init() {
// Create client target.
client_target_ = new HWCLayer(id_, buffer_allocator_);
// Calls into SDM need to be dropped. Create Null Display interface.
display_intf_ = new DisplayNull();
disable_animation_ = Debug::IsExtAnimDisabled();
return HWCDisplayVirtual::Init();
}
int HWCDisplayVirtualGPU::Deinit() {
// Destory color convert instance. This destroys thread and underlying GL resources.
if (gl_color_convert_) {
color_convert_task_.PerformTask(ColorConvertTaskCode::kCodeDestroyInstance, nullptr);
}
delete static_cast<DisplayNull *>(display_intf_);
delete client_target_;
for (auto hwc_layer : layer_set_) {
delete hwc_layer;
}
return 0;
}
HWCDisplayVirtualGPU::HWCDisplayVirtualGPU(CoreInterface *core_intf, HWCBufferAllocator
*buffer_allocator, HWCCallbacks *callbacks,
hwc2_display_t id, int32_t sdm_id, uint32_t width,
uint32_t height, float min_lum, float max_lum) :
HWCDisplayVirtual(core_intf, buffer_allocator, callbacks, id, sdm_id, width, height),
color_convert_task_(*this) {
}
HWC2::Error HWCDisplayVirtualGPU::Validate(uint32_t *out_num_types, uint32_t *out_num_requests) {
DTRACE_SCOPED();
// Reset previous changes.
layer_changes_.clear();
layer_requests_.clear();
// Mark all layers to GPU if there is no need to bypass.
bool fbt_compatible = true;
bool needs_gpu_bypass = NeedsGPUBypass() || FreezeScreen();
for (auto hwc_layer : layer_set_) {
auto layer = hwc_layer->GetSDMLayer();
layer->composition = needs_gpu_bypass ? kCompositionSDE : kCompositionGPU;
if (needs_gpu_bypass) {
if (hwc_layer->GetClientRequestedCompositionType() == HWC2::Composition::Client) {
layer_changes_[hwc_layer->GetId()] = HWC2::Composition::Device;
layer_requests_[hwc_layer->GetId()] = HWC2::LayerRequest::ClearClientTarget;
}
} else {
if (hwc_layer->GetClientRequestedCompositionType() != HWC2::Composition::Client) {
layer_changes_[hwc_layer->GetId()] = HWC2::Composition::Client;
}
}
}
// Derive client target dataspace based on the color mode - bug/115482728
int32_t client_target_dataspace = GetDataspaceFromColorMode(GetCurrentColorMode());
SetClientTargetDataSpace(client_target_dataspace);
*out_num_types = UINT32(layer_changes_.size());
*out_num_requests = UINT32(layer_requests_.size());;
has_client_composition_ = !needs_gpu_bypass;
// FBT is compatible if all layers are compatible or gpu is bypassed.
fbt_compatible_ = has_client_composition_ && fbt_compatible;
return ((*out_num_types > 0) ? HWC2::Error::HasChanges : HWC2::Error::None);
}
HWC2::Error HWCDisplayVirtualGPU::CommitOrPrepare(bool validate_only,
shared_ptr<Fence> *out_retire_fence,
uint32_t *out_num_types,
uint32_t *out_num_requests, bool *needs_commit) {
// Perform validate and commit.
auto status = Validate(out_num_types, out_num_requests);
if (!fbt_compatible_) {
*needs_commit = true;
return status;
}
return Present(out_retire_fence);
}
HWC2::Error HWCDisplayVirtualGPU::SetOutputBuffer(buffer_handle_t buf,
shared_ptr<Fence> release_fence) {
HWC2::Error error = HWCDisplayVirtual::SetOutputBuffer(buf, release_fence);
if (error != HWC2::Error::None) {
return error;
}
native_handle_t *hnd = const_cast<native_handle_t *>(buf);
buffer_allocator_->GetWidth(hnd, output_buffer_.width);
buffer_allocator_->GetHeight(hnd, output_buffer_.height);
buffer_allocator_->GetUnalignedWidth(hnd, output_buffer_.unaligned_width);
buffer_allocator_->GetUnalignedHeight(hnd, output_buffer_.unaligned_height);
// Update active dimensions.
if (qtigralloc::getMetadataState(hnd, android::gralloc4::MetadataType_Crop.value)) {
int32_t slice_width = 0, slice_height = 0;
if (!buffer_allocator_->GetBufferGeometry(hnd, slice_width, slice_height)) {
output_buffer_.unaligned_width = slice_width;
output_buffer_.unaligned_height = slice_height;
color_convert_task_.PerformTask(ColorConvertTaskCode::kCodeReset, nullptr);
}
}
return HWC2::Error::None;
}
HWC2::Error HWCDisplayVirtualGPU::Present(shared_ptr<Fence> *out_retire_fence) {
DTRACE_SCOPED();
auto status = HWC2::Error::None;
if (!output_buffer_.buffer_id) {
return HWC2::Error::NoResources;
}
if (NeedsGPUBypass()) {
return status;
}
layer_stack_.output_buffer = &output_buffer_;
// Ensure that blit is initialized.
// GPU context gets in secure or non-secure mode depending on output buffer provided.
if (!gl_color_convert_) {
// Get instance.
color_convert_task_.PerformTask(ColorConvertTaskCode::kCodeGetInstance, nullptr);
if (gl_color_convert_ == nullptr) {
DLOGE("Failed to get Color Convert Instance");
return HWC2::Error::NoResources;
} else {
DLOGI("Created ColorConvert instance: %p", gl_color_convert_);
}
}
ColorConvertBlitContext ctx = {};
Layer *sdm_layer = client_target_->GetSDMLayer();
LayerBuffer &input_buffer = sdm_layer->input_buffer;
ctx.src_hnd = reinterpret_cast<const native_handle_t *>(input_buffer.buffer_id);
ctx.dst_hnd = reinterpret_cast<const native_handle_t *>(output_handle_);
ctx.dst_rect = {0, 0, FLOAT(output_buffer_.unaligned_width),
FLOAT(output_buffer_.unaligned_height)};
ctx.src_acquire_fence = input_buffer.acquire_fence;
ctx.dst_acquire_fence = output_buffer_.acquire_fence;
color_convert_task_.PerformTask(ColorConvertTaskCode::kCodeBlit, &ctx);
// todo blit
DumpVDSBuffer();
*out_retire_fence = ctx.release_fence;
return status;
}
void HWCDisplayVirtualGPU::OnTask(const ColorConvertTaskCode &task_code,
SyncTask<ColorConvertTaskCode>::TaskContext *task_context) {
switch (task_code) {
case ColorConvertTaskCode::kCodeGetInstance: {
gl_color_convert_ = GLColorConvert::GetInstance(kTargetYUV, output_buffer_.flags.secure);
}
break;
case ColorConvertTaskCode::kCodeBlit: {
DTRACE_SCOPED();
ColorConvertBlitContext* ctx = reinterpret_cast<ColorConvertBlitContext*>(task_context);
gl_color_convert_->Blit(ctx->src_hnd, ctx->dst_hnd, ctx->src_rect, ctx->dst_rect,
ctx->src_acquire_fence, ctx->dst_acquire_fence,
&(ctx->release_fence));
}
break;
case ColorConvertTaskCode::kCodeReset: {
DTRACE_SCOPED();
if (gl_color_convert_) {
gl_color_convert_->Reset();
}
}
break;
case ColorConvertTaskCode::kCodeDestroyInstance: {
if (gl_color_convert_) {
GLColorConvert::Destroy(gl_color_convert_);
}
}
break;
}
}
bool HWCDisplayVirtualGPU::FreezeScreen() {
if (!disable_animation_) {
return false;
}
bool freeze_screen = false;
if (animating_ && !animation_in_progress_) {
// Start of animation. GPU comp is needed.
animation_in_progress_ = true;
} else if (!animating_ && animation_in_progress_) {
// End of animation. Start composing.
animation_in_progress_ = false;
} else if (animating_ && animation_in_progress_) {
// Animation in progress...
freeze_screen = true;
}
return freeze_screen;
}
} // namespace sdm