Skip to content

Commit

Permalink
RetroPlayer: Add support for zero-copy buffers
Browse files Browse the repository at this point in the history
  • Loading branch information
garbear committed Jul 5, 2018
1 parent f7a462a commit 3c3b53e
Show file tree
Hide file tree
Showing 13 changed files with 214 additions and 17 deletions.
73 changes: 65 additions & 8 deletions xbmc/cores/RetroPlayer/rendering/RPRenderManager.cpp
Expand Up @@ -77,6 +77,10 @@ void CRPRenderManager::Deinitialize()
renderBuffer->Release();
m_renderBuffers.clear();

for (auto buffer : m_pendingBuffers)
buffer->Release();
m_pendingBuffers.clear();

m_renderers.clear();

m_state = RENDER_STATE::UNCONFIGURED;
Expand Down Expand Up @@ -113,6 +117,41 @@ bool CRPRenderManager::Configure(AVPixelFormat format, unsigned int nominalWidth
return true;
}

bool CRPRenderManager::GetVideoBuffer(unsigned int width, unsigned int height, AVPixelFormat &format, uint8_t *&data, size_t &size)
{
if (m_bFlush || m_state != RENDER_STATE::CONFIGURED)
return false;

for (IRenderBuffer *buffer : m_pendingBuffers)
buffer->Release();
m_pendingBuffers.clear();

// Get buffers from visible renderers
for (IRenderBufferPool *bufferPool : m_processInfo.GetBufferManager().GetBufferPools())
{
if (!bufferPool->HasVisibleRenderer())
continue;

IRenderBuffer *renderBuffer = bufferPool->GetBuffer(width, height);
if (renderBuffer != nullptr)
m_pendingBuffers.emplace_back(renderBuffer);
else
CLog::Log(LOGDEBUG, "RetroPlayer[RENDER]: Unable to get video buffer for frame");
}

if (m_pendingBuffers.empty())
return false;

//! @todo Handle multiple buffers
IRenderBuffer *renderBuffer = m_pendingBuffers.at(0);

format = renderBuffer->GetFormat();
data = renderBuffer->GetMemory();
size = renderBuffer->GetFrameSize();

return true;
}

void CRPRenderManager::AddFrame(const uint8_t* data, size_t size, unsigned int width, unsigned int height, unsigned int orientationDegCCW)
{
if (m_bFlush || m_state != RENDER_STATE::CONFIGURED)
Expand All @@ -129,18 +168,36 @@ void CRPRenderManager::AddFrame(const uint8_t* data, size_t size, unsigned int w
return;
}

// Copy frame to buffers with visible renderers
// Get render buffers to copy the frame into
std::vector<IRenderBuffer*> renderBuffers;
for (IRenderBufferPool *bufferPool : m_processInfo.GetBufferManager().GetBufferPools())

// Check pending buffers
for (IRenderBuffer *buffer : m_pendingBuffers)
{
if (!bufferPool->HasVisibleRenderer())
continue;
if (buffer->GetMemory() == data)
{
buffer->Acquire();
renderBuffers.emplace_back(buffer);
}
}

IRenderBuffer *renderBuffer = bufferPool->GetBuffer(width, height);
if (renderBuffer != nullptr)
// If we aren't submitting a zero-copy frame, copy into render buffer now
if (renderBuffers.empty())
{
// Copy frame to buffers with visible renderers
for (IRenderBufferPool *bufferPool : m_processInfo.GetBufferManager().GetBufferPools())
{
CopyFrame(renderBuffer, m_format, data, size, width, height);
renderBuffers.emplace_back(renderBuffer);
if (!bufferPool->HasVisibleRenderer())
continue;

IRenderBuffer *renderBuffer = bufferPool->GetBuffer(width, height);
if (renderBuffer != nullptr)
{
CopyFrame(renderBuffer, m_format, data, size, width, height);
renderBuffers.emplace_back(renderBuffer);
}
else
CLog::Log(LOGDEBUG, "RetroPlayer[RENDER]: Unable to get render buffer for frame");
}
}

Expand Down
2 changes: 2 additions & 0 deletions xbmc/cores/RetroPlayer/rendering/RPRenderManager.h
Expand Up @@ -88,6 +88,7 @@ namespace RETRO

// Functions called from game loop
bool Configure(AVPixelFormat format, unsigned int nominalWidth, unsigned int nominalHeight, unsigned int maxWidth, unsigned int maxHeight);
bool GetVideoBuffer(unsigned int width, unsigned int height, AVPixelFormat &format, uint8_t *&data, size_t &size);
void AddFrame(const uint8_t* data, size_t size, unsigned int width, unsigned int height, unsigned int orientationDegCW);

// Functions called from the player
Expand Down Expand Up @@ -189,6 +190,7 @@ namespace RETRO

// Render resources
std::set<std::shared_ptr<CRPBaseRenderer>> m_renderers;
std::vector<IRenderBuffer*> m_pendingBuffers; // Only access from game thread
std::vector<IRenderBuffer*> m_renderBuffers;
std::map<AVPixelFormat, SwsContext*> m_scalers;
std::vector<uint8_t> m_cachedFrame;
Expand Down
5 changes: 1 addition & 4 deletions xbmc/cores/RetroPlayer/streams/RPStreamManager.cpp
Expand Up @@ -52,12 +52,9 @@ StreamPtr CRPStreamManager::CreateStream(StreamType streamType)
return StreamPtr(m_audioStream);
}
case StreamType::VIDEO:
{
return StreamPtr(new CRetroPlayerVideo(m_renderManager, m_processInfo));
}
case StreamType::SW_BUFFER:
{
//return StreamPtr(new CRetroPlayerSoftware(m_renderManager, m_processInfo)); //! @todo
return StreamPtr(new CRetroPlayerVideo(m_renderManager, m_processInfo));
}
case StreamType::HW_BUFFER:
{
Expand Down
16 changes: 16 additions & 0 deletions xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.cpp
Expand Up @@ -77,6 +77,22 @@ bool CRetroPlayerVideo::OpenStream(const StreamProperties& properties)
return m_bOpen;
}

bool CRetroPlayerVideo::GetStreamBuffer(unsigned int width, unsigned int height, StreamBuffer& buffer)
{
VideoStreamBuffer& videoBuffer = reinterpret_cast<VideoStreamBuffer&>(buffer);

if (m_bOpen)
{
return m_renderManager.GetVideoBuffer(width,
height,
videoBuffer.pixfmt,
videoBuffer.data,
videoBuffer.size);
}

return false;
}

void CRetroPlayerVideo::AddStreamData(const StreamPacket &packet)
{
const VideoStreamPacket& videoPacket = reinterpret_cast<const VideoStreamPacket&>(packet);
Expand Down
9 changes: 8 additions & 1 deletion xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.h
Expand Up @@ -43,6 +43,13 @@ namespace RETRO
float pixelAspectRatio;
};

struct VideoStreamBuffer
{
AVPixelFormat pixfmt;
uint8_t *data;
size_t size;
};

struct VideoStreamPacket
{
unsigned int width;
Expand All @@ -65,7 +72,7 @@ namespace RETRO

// implementation of IRetroPlayerStream
bool OpenStream(const StreamProperties& properties) override;
bool GetStreamBuffer(unsigned int width, unsigned int height, StreamBuffer& buffer) override { return false; }
bool GetStreamBuffer(unsigned int width, unsigned int height, StreamBuffer& buffer) override;
void AddStreamData(const StreamPacket &packet) override;
void CloseStream() override;

Expand Down
13 changes: 13 additions & 0 deletions xbmc/games/addons/GameClientTranslator.cpp
Expand Up @@ -94,6 +94,19 @@ AVPixelFormat CGameClientTranslator::TranslatePixelFormat(GAME_PIXEL_FORMAT form
return AV_PIX_FMT_NONE;
}

GAME_PIXEL_FORMAT CGameClientTranslator::TranslatePixelFormat(AVPixelFormat format)
{
switch (format)
{
case AV_PIX_FMT_0RGB32: return GAME_PIXEL_FORMAT_0RGB8888;
case AV_PIX_FMT_RGB565: return GAME_PIXEL_FORMAT_RGB565;
case AV_PIX_FMT_RGB555: return GAME_PIXEL_FORMAT_0RGB1555;
default:
break;
}
return GAME_PIXEL_FORMAT_UNKNOWN;
}

RETRO::PCMFormat CGameClientTranslator::TranslatePCMFormat(GAME_PCM_FORMAT format)
{
switch (format)
Expand Down
7 changes: 7 additions & 0 deletions xbmc/games/addons/GameClientTranslator.h
Expand Up @@ -73,6 +73,13 @@ namespace GAME
*/
static AVPixelFormat TranslatePixelFormat(GAME_PIXEL_FORMAT format);

/*!
* \brief Translate pixel format (RetroPlayer/FFMPEG to Game API).
* \param format The pixel format to translate.
* \return Translated pixel format.
*/
static GAME_PIXEL_FORMAT TranslatePixelFormat(AVPixelFormat format);

/*!
* \brief Translate audio PCM format (Game API to RetroPlayer).
* \param format The audio PCM format to translate.
Expand Down
2 changes: 2 additions & 0 deletions xbmc/games/addons/streams/CMakeLists.txt
@@ -1,10 +1,12 @@
set(SOURCES GameClientStreamAudio.cpp
GameClientStreams.cpp
GameClientStreamSwFramebuffer.cpp
GameClientStreamVideo.cpp
)

set(HEADERS GameClientStreamAudio.h
GameClientStreams.h
GameClientStreamSwFramebuffer.h
GameClientStreamVideo.h
IGameClientStream.h
)
Expand Down
49 changes: 49 additions & 0 deletions xbmc/games/addons/streams/GameClientStreamSwFramebuffer.cpp
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2018 Team Kodi
* http://kodi.tv
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this Program; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*
*/

#include "GameClientStreamSwFramebuffer.h"
#include "addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h"
#include "cores/RetroPlayer/streams/RetroPlayerVideo.h"
#include "games/addons/GameClientTranslator.h"

using namespace KODI;
using namespace GAME;

bool CGameClientStreamSwFramebuffer::GetBuffer(unsigned int width, unsigned int height, game_stream_buffer &buffer)
{
if (m_stream != nullptr)
{
RETRO::VideoStreamBuffer streamBuffer;
if (m_stream->GetStreamBuffer(width, height, reinterpret_cast<RETRO::StreamBuffer&>(streamBuffer)))
{
buffer.type = GAME_STREAM_SW_FRAMEBUFFER;

game_stream_sw_framebuffer_buffer& framebuffer = buffer.sw_framebuffer;

framebuffer.format = CGameClientTranslator::TranslatePixelFormat(streamBuffer.pixfmt);
framebuffer.data = streamBuffer.data;
framebuffer.size = streamBuffer.size;

return true;
}
}

return false;
}
40 changes: 40 additions & 0 deletions xbmc/games/addons/streams/GameClientStreamSwFramebuffer.h
@@ -0,0 +1,40 @@
/*
* Copyright (C) 2018 Team Kodi
* http://kodi.tv
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this Program; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*
*/
#pragma once

#include "GameClientStreamVideo.h"

namespace KODI
{
namespace GAME
{

class CGameClientStreamSwFramebuffer : public CGameClientStreamVideo
{
public:
CGameClientStreamSwFramebuffer() = default;
~CGameClientStreamSwFramebuffer() override = default;

// Implementation of IGameClientStream via CGameClientStreamVideo
bool GetBuffer(unsigned int width, unsigned int height, game_stream_buffer &buffer) override;
};

} // namespace GAME
} // namespace KODI
2 changes: 1 addition & 1 deletion xbmc/games/addons/streams/GameClientStreamVideo.cpp
Expand Up @@ -56,7 +56,7 @@ void CGameClientStreamVideo::CloseStream()

void CGameClientStreamVideo::AddData(const game_stream_packet& packet)
{
if (packet.type != GAME_STREAM_VIDEO)
if (packet.type != GAME_STREAM_VIDEO && packet.type != GAME_STREAM_SW_FRAMEBUFFER)
return;

if (m_stream != nullptr)
Expand Down
7 changes: 4 additions & 3 deletions xbmc/games/addons/streams/GameClientStreamVideo.h
Expand Up @@ -47,12 +47,13 @@ class CGameClientStreamVideo : public IGameClientStream
void CloseStream() override;
void AddData(const game_stream_packet& packet) override;

protected:
// Stream parameters
RETRO::IRetroPlayerStream* m_stream = nullptr;

private:
// Utility functions
static RETRO::VideoStreamProperties* TranslateProperties(const game_stream_video_properties &properties);

// Stream parameters
RETRO::IRetroPlayerStream* m_stream = nullptr;
};

} // namespace GAME
Expand Down
6 changes: 6 additions & 0 deletions xbmc/games/addons/streams/GameClientStreams.cpp
Expand Up @@ -20,6 +20,7 @@

#include "GameClientStreams.h"
#include "GameClientStreamAudio.h"
#include "GameClientStreamSwFramebuffer.h"
#include "GameClientStreamVideo.h"
#include "cores/RetroPlayer/streams/IRetroPlayerStream.h"
#include "cores/RetroPlayer/streams/IStreamManager.h"
Expand Down Expand Up @@ -113,6 +114,11 @@ std::unique_ptr<IGameClientStream> CGameClientStreams::CreateStream(GAME_STREAM_
gameStream.reset(new CGameClientStreamVideo);
break;
}
case GAME_STREAM_SW_FRAMEBUFFER:
{
gameStream.reset(new CGameClientStreamSwFramebuffer);
break;
}
default:
break;
}
Expand Down

0 comments on commit 3c3b53e

Please sign in to comment.