blob: 4679702ce2a43f5ba14f1b2024bb64a00da53b9d [file] [log] [blame]
/*
* Copyright (C) 2021 Amlogic Corporation.
*
* 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 <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/epoll.h>
#include <string.h>
#include <stdlib.h>
#include <wayland-client.h>
#include "xdg-shell-client-protocol.h"
#include "viewporter-client-protocol.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
#include "fullscreen-shell-unstable-v1-client-protocol.h"
#include "wayland_display.h"
#include "wayland_buffer.h"
#include "Logger.h"
#include "wayland_videoformat.h"
#include "wayland_dma.h"
#include "wayland_shm.h"
#include "Utils.h"
#include "ErrorCode.h"
#include "Times.h"
#define TAG "rlib:wayland_buffer"
#define ENABLE_MORE_LOG (0)
WaylandBuffer::WaylandBuffer(WaylandDisplay *display, int logCategory, int cookieId)
: mDisplay(display),
mLogCategory(logCategory)
{
mRenderBuffer = NULL;
mWaylandWlWrap = NULL;
mUsedByCompositor = false;
mRedrawingPending = false;
mRealTime = -1;
mBufferFormat = VIDEO_FORMAT_UNKNOWN;
mFrameWidth = 0;
mFrameHeight = 0;
mState = BUFFER_IDLE;
mCookieId = cookieId;
if (ENABLE_MORE_LOG) {
TRACE(mLogCategory,"new WaylandBuffer:%p,cookie:%d",this, mCookieId);
}
}
WaylandBuffer::~WaylandBuffer()
{
if (ENABLE_MORE_LOG) {
TRACE(mLogCategory,"delete WaylandBuffer:%p,DmaBuffer:%p,cookie:%d",this,mWaylandWlWrap,mCookieId);
}
/*if weston obtains the wl_buffer,we need
* notify user to release renderBuffer*/
if (mRenderBuffer) {
mDisplay->handleBufferReleaseCallback(this);
mRenderBuffer = NULL;
}
if (mWaylandWlWrap) {
delete mWaylandWlWrap;
mWaylandWlWrap = NULL;
}
}
void WaylandBuffer::bufferRelease (void *data, struct wl_buffer *wl_buffer)
{
WaylandBuffer* waylandBuffer = static_cast<WaylandBuffer*>(data);
TRACE(waylandBuffer->mLogCategory,"--wl_buffer:%p(%p),renderBuffer:%p",wl_buffer,waylandBuffer,waylandBuffer->mRenderBuffer);
waylandBuffer->mUsedByCompositor = false;
waylandBuffer->mState = BUFFER_IDLE;
//sometimes this callback be called twice
//this cause double free,so check mRenderBuffer
if (waylandBuffer->mRenderBuffer) {
waylandBuffer->mDisplay->handleBufferReleaseCallback(waylandBuffer);
waylandBuffer->mLock.lock();
waylandBuffer->mRenderBuffer = NULL;
waylandBuffer->mRealTime = -1;
waylandBuffer->mLock.unlock();
}
//resolution changed,release buffers before resolution changed
if ((waylandBuffer->mCookieId < waylandBuffer->mDisplay->getWaylandBufferCookieId()) &&
waylandBuffer->mWaylandWlWrap) {
TRACE(waylandBuffer->mLogCategory,"drs,delete WaylandBuffer:%p,WaylandDmaBuffer:%p",waylandBuffer,waylandBuffer->mWaylandWlWrap);
waylandBuffer->mLock.lock();
delete waylandBuffer->mWaylandWlWrap;
waylandBuffer->mWaylandWlWrap = NULL;
waylandBuffer->mLock.unlock();
}
}
void WaylandBuffer::bufferdroped (void *data, struct wl_buffer *wl_buffer)
{
WaylandBuffer* waylandBuffer = static_cast<WaylandBuffer*>(data);
WARNING(waylandBuffer->mLogCategory,"--dropped wl_buffer:%p(%p),renderBuffer:%p",wl_buffer,waylandBuffer,waylandBuffer->mRenderBuffer);
waylandBuffer->mUsedByCompositor = false;
waylandBuffer->mState = BUFFER_IDLE;
if (waylandBuffer->mRenderBuffer) {
waylandBuffer->mDisplay->handleFrameDropedCallback(waylandBuffer);
waylandBuffer->mDisplay->handleBufferReleaseCallback(waylandBuffer);
waylandBuffer->mRenderBuffer = NULL;
}
}
void WaylandBuffer::bufferDisplayTime (void *data, struct wl_buffer *wl_buffer, uint32_t sec, uint32_t usec)
{
WaylandBuffer* waylandBuffer = static_cast<WaylandBuffer*>(data);
if (ENABLE_MORE_LOG) {
TRACE(waylandBuffer->mLogCategory,"wl_buffer:%p(%p)",wl_buffer,waylandBuffer);
}
if (waylandBuffer->mRenderBuffer) {
waylandBuffer->mDisplay->handleFrameTime(waylandBuffer, sec, usec);
}
}
static const struct wl_buffer_listener buffer_with_drop_listener = {
WaylandBuffer::bufferRelease,
WaylandBuffer::bufferdroped,
WaylandBuffer::bufferDisplayTime,
};
static const struct wl_buffer_listener buffer_listener = {
WaylandBuffer::bufferRelease
};
/*if we commit buffers to weston too fast,it causes weston can't invoke this callback*/
void WaylandBuffer::frameCallback(void *data, struct wl_callback *callback, uint32_t time)
{
WaylandBuffer* waylandBuffer = static_cast<WaylandBuffer*>(data);
waylandBuffer->mDisplay->setRedrawingPending(false);
int64_t elapsed = 0;
int64_t nowTimeUs = Tls::Times::getSystemTimeUs();
//debug for detecting framecallback elapsed time
if (waylandBuffer->mDisplay->frameCallbackTimeUs == 0) {
waylandBuffer->mDisplay->frameCallbackTimeUs = nowTimeUs;
} else {
elapsed = (nowTimeUs - waylandBuffer->mDisplay->frameCallbackTimeUs)/1000;
waylandBuffer->mDisplay->frameCallbackTimeUs = nowTimeUs;
}
waylandBuffer->mLock.lock();
bool redrawing = waylandBuffer->mRedrawingPending;
waylandBuffer->mRedrawingPending = false;
waylandBuffer->mLock.unlock();
TRACE(waylandBuffer->mLogCategory,"--WaylandBuffer:%p,renderBuffer:%p,elapsed:%lld ms",waylandBuffer,waylandBuffer->mRenderBuffer,elapsed);
if (waylandBuffer->mRenderBuffer && redrawing) {
waylandBuffer->mDisplay->handleFrameDisplayedCallback(waylandBuffer);
}
wl_callback_destroy (callback);
}
static const struct wl_callback_listener frame_callback_listener = {
WaylandBuffer::frameCallback
};
int WaylandBuffer::constructWlBuffer(RenderBuffer *buf)
{
struct wl_buffer * wlbuffer = NULL;
WaylandDisplay::AmlConfigAPIList *amlConfigAPI = mDisplay->getAmlConfigAPIList();
mLock.lock();
mRenderBuffer = buf;
mState = BUFFER_PREPARED;
if (mWaylandWlWrap) {
mLock.unlock();
//TRACE(mLogCategory,"WaylandBuffer:%p,WaylandDmaBuffer:%p,renderBuffer:%p,pts:%lld us,cookie:%d",this,mWaylandWlWrap,mRenderBuffer,buf->pts/1000,mCookieId);
return NO_ERROR;
}
mLock.unlock();
if (buf->flag & BUFFER_FLAG_DMA_BUFFER) {
WaylandDmaBuffer *waylanddma = new WaylandDmaBuffer(mDisplay, mLogCategory);
wlbuffer = waylanddma->constructWlBuffer(&buf->dma, mBufferFormat);
if (!wlbuffer) {
mState = BUFFER_IDLE;
delete waylanddma;
ERROR(mLogCategory,"create wl_buffer fail");
return ERROR_INVALID_OPERATION;
}
mWaylandWlWrap = waylanddma;
mFrameWidth = buf->dma.width;
mFrameHeight = buf->dma.height;
if (ENABLE_MORE_LOG) {
TRACE(mLogCategory,"new WaylandBuffer:%p,WaylandDmaBuffer:%p,pts:%lld us %dx%d cookie:%d",
this,mWaylandWlWrap,buf->pts/1000,mFrameWidth,mFrameHeight,mCookieId);
}
}
/*register buffer release listen*/
if (amlConfigAPI->enableDropFrame || amlConfigAPI->enableDisplayTime) {
wl_buffer_add_listener (wlbuffer, &buffer_with_drop_listener, this);
} else {
wl_buffer_add_listener (wlbuffer, &buffer_listener, this);
}
return NO_ERROR;
}
struct wl_buffer *WaylandBuffer::getWlBuffer()
{
if (mWaylandWlWrap) {
return mWaylandWlWrap->getWlBuffer();
} else {
return NULL;
}
}
void WaylandBuffer::attach(struct wl_surface *surface)
{
struct wl_callback *callback;
struct wl_buffer *wlbuffer = NULL;
if (mUsedByCompositor) {
DEBUG(mLogCategory,"buffer used by compositor");
return;
}
//callback when this frame displayed
callback = wl_surface_frame (surface);
wl_callback_add_listener (callback, &frame_callback_listener, this);
mDisplay->setRedrawingPending(true);
wlbuffer = getWlBuffer();
if (wlbuffer) {
wl_surface_attach (surface, wlbuffer, 0, 0);
}
mState = BUFFER_DISPLAYING;
mUsedByCompositor = true;
mRedrawingPending = true;
}
/**
* @brief check wayland buffer is free. if this is free
* ,it will be delete by waylandbuffer manager
*
* @return true if this buffer is free
* @return false if this buffer will be used
*/
bool WaylandBuffer::isFree()
{
Tls::Mutex::Autolock _l(mLock);
if (!mWaylandWlWrap && !mRenderBuffer) {
return true;
}
/*if resolution changed,this buffer should be deleted after weston
release this buffer */
if (mState == BUFFER_IDLE &&
mCookieId != mDisplay->getWaylandBufferCookieId()) {
return true;
}
return false;
};
void WaylandBuffer::forceBufferStateIdle()
{
mState = BUFFER_IDLE;
mRenderBuffer = NULL;
if (ENABLE_MORE_LOG) {
TRACE(mLogCategory,"WaylandBuffer:%p,DmaBuffer:%p,cookie:%d",this,mWaylandWlWrap,mCookieId);
}
}