Skip to content

Commit

Permalink
Merge pull request xbmc#26091 from garbear/retro-gl-plumbing
Browse files Browse the repository at this point in the history
RetroPlayer: OpenGL Plumbing
  • Loading branch information
garbear authored Dec 21, 2024
2 parents 4935f3e + a76903a commit f4dd6df
Show file tree
Hide file tree
Showing 31 changed files with 760 additions and 18 deletions.
8 changes: 7 additions & 1 deletion addons/resource.language.en_gb/resources/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -19152,7 +19152,13 @@ msgctxt "#35270"
msgid "Your account is not verified. Please check your email to complete your sign up."
msgstr ""

#empty strings from id 35271 to 35504
#. Error message shown when an OpenGL game is played, as OpenGL is not currently supported
#: xbmc/games/addons/GameClient.cpp
msgctxt "#35271"
msgid "This game requires OpenGL support for 3D rendering. OpenGL support is still under development."
msgstr ""

#empty strings from id 35272 to 35504

#. connection state "host unreachable"
#: xbmc/pvr/addons/PVRClients.cpp
Expand Down
15 changes: 15 additions & 0 deletions xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/Game.h
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,21 @@ class ATTR_DLL_LOCAL CInstanceGame : public IAddonInstance
///
///@{

//==========================================================================
/// @brief **Callback to Kodi Function**\n
/// Enable hardware rendering functionality
///
/// @return True if hardware rendering was enabled, false otherwise
///
/// @remarks Only called from addon itself
///
bool EnableHardwareRendering(const game_hw_rendering_properties& properties)
{
return m_instanceData->toKodi->EnableHardwareRendering(m_instanceData->toKodi->kodiInstance,
&properties);
}
//----------------------------------------------------------------------------

//============================================================================
/// @brief Invalidates the current HW context and reinitializes GPU resources
///
Expand Down
24 changes: 21 additions & 3 deletions xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,10 +318,14 @@ extern "C"
} GAME_HW_CONTEXT_TYPE;
//----------------------------------------------------------------------------

//============================================================================
/// @brief **Hardware framebuffer properties**
//==============================================================================
/// @brief **Hardware rendering properties**
///
typedef struct game_stream_hw_framebuffer_properties
/// These properties are needed early on, so instead of passing them when the
/// stream is opened, they are passed in EnableHardwareRendering(). As a
/// result, the struct passed to OpenStream() is empty.
///
typedef struct game_hw_rendering_properties
{
/// @brief The API to use.
///
Expand Down Expand Up @@ -371,6 +375,19 @@ extern "C"

/// @brief Creates a debug context.
bool debug_context;

} ATTR_PACKED game_hw_rendering_properties;
//------------------------------------------------------------------------------

//==============================================================================
/// @brief **Hardware framebuffer properties**
///
/// This struct is empty because hardware rendering properties are passed via
/// EnableHardwareRendering().
///
typedef struct game_stream_hw_framebuffer_properties
{
char dummy; // Occupier for empty struct
} ATTR_PACKED game_stream_hw_framebuffer_properties;
//----------------------------------------------------------------------------

Expand Down Expand Up @@ -1193,6 +1210,7 @@ extern "C"
{
KODI_HANDLE kodiInstance;

bool (*EnableHardwareRendering)(void*, const game_hw_rendering_properties*);
void (*CloseGame)(KODI_HANDLE kodiInstance);
KODI_GAME_STREAM_HANDLE (*OpenStream)(KODI_HANDLE, const struct game_stream_properties*);
bool (*GetStreamBuffer)(KODI_HANDLE,
Expand Down
4 changes: 2 additions & 2 deletions xbmc/addons/kodi-dev-kit/include/kodi/versions.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@
#define ADDON_INSTANCE_VERSION_AUDIOENCODER_DEPENDS "c-api/addon-instance/audioencoder.h" \
"addon-instance/AudioEncoder.h"

#define ADDON_INSTANCE_VERSION_GAME "3.0.2"
#define ADDON_INSTANCE_VERSION_GAME_MIN "3.0.0"
#define ADDON_INSTANCE_VERSION_GAME "4.0.0"
#define ADDON_INSTANCE_VERSION_GAME_MIN "4.0.0"
#define ADDON_INSTANCE_VERSION_GAME_XML_ID "kodi.binary.instance.game"
#define ADDON_INSTANCE_VERSION_GAME_DEPENDS "addon-instance/Game.h"

Expand Down
70 changes: 70 additions & 0 deletions xbmc/cores/RetroPlayer/OpenGL_Roadmap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# OpenGL Roadmap for RetroPlayer

OpenGL support in RetroPlayer will enable hardware rendering for cores that require OpenGL. This roadmap outlines the key tasks needed to complete its integration in Kodi.

The OpenGL tracking issue is: https://github.com/xbmc/xbmc/issues/17743

## Current Status

The foundational work for OpenGL support in RetroPlayer has been merged, including:

- **Plumbing APIs:** RetroPlayer APIs now support OpenGL procedures and framebuffer streams ([PR #26091](https://github.com/xbmc/xbmc/pull/26091))
- **Integration with Game API:** The Game API has been extended to connect libretro cores with OpenGL rendering functionality.
- **Core Compatibility:** Initial testing has verified that OpenGL cores, such as Mupen64Plus and Beetle PSX HW, can interface with RetroPlayer without crashing.

## Remaining Work

### VAOs and Rendering Pipeline

The use of Vertex Array Objects (VAOs) is a critical step in modernizing RetroPlayer's rendering pipeline. VAOs streamline the management of vertex attributes, making the OpenGL rendering process more efficient and less error-prone.

- **Current Progress:** The removal of global VAOs has been addressed in [PR #22211: Remove Global VAOs](https://github.com/xbmc/xbmc/pull/22211), which simplifies the rendering pipeline.
- **Next Step:** Review and merge [PR #22211](https://github.com/xbmc/xbmc/pull/22211) to finalize VAO updates.
- The PR has been rebased here: https://github.com/garbear/xbmc/commits/retro-gl-v3

Once the VAO work is completed, RetroPlayer will benefit from a cleaner and more maintainable rendering architecture.

### Frame Buffer Object (FBO) Renderbuffers

Mostly done. Original work [here](https://github.com/lrusak/xbmc/commits/retro-gl-v3) and rebased work [here](https://github.com/garbear/xbmc/commits/retro-gl-v3).

### Synchronization

The main synchronization challenges for OpenGL support in RetroPlayer stem from the separation of the game loop (which updates game state) and the rendering thread (which processes graphics).

- **GL Context Management:**
OpenGL contexts are bound to threads, requiring careful management to ensure that rendering operations occur exclusively on the rendering thread. Context sharing between threads needs to be avoided or strictly synchronized.

- **Dynamic Resolution Changes:**
Dynamic resolution adjustments during gameplay add complexity. Both threads must coordinate to resize and reallocate frame buffers seamlessly, without causing crashes or visual glitches.

#### GL Context Management

Managing OpenGL contexts in RetroPlayer presents unique challenges due to its multithreaded architecture. Key considerations include:

- **Thread Binding:**
OpenGL contexts are thread-specific. RetroPlayer must ensure that rendering occurs on a dedicated rendering thread without interfering with the game loop.

- **Context Resetting:**
When switching contexts (e.g., during resolution changes), RetroPlayer must reinitialize OpenGL state while avoiding disruptions to ongoing operations.

- **Multiple Platforms:**
Context management strategies must accommodate platform-specific OpenGL implementations, such as OpenGL ES for ARM-based devices or Metal wrappers for macOS.

To address these challenges, RetroPlayer can leverage context creation and management best practices, such as ensuring all rendering occurs on the same thread and using thread-safe proxy functions for OpenGL operations initiated by the game loop.

#### Dynamic Resolution Changes

Needed to accommodate gameplay scenarios where resolution changes are required mid-session, such as switching between fullscreen and windowed modes or adapting to performance constraints. This requires:

- **Real-Time Buffer Resizing:**
Ensure frame buffers are resized and reallocated without disrupting gameplay or introducing visual artifacts.

- **Synchronization with Rendering Thread:**
Both the game loop and the rendering thread must coordinate to apply resolution changes seamlessly.

---

## Conclusion

RetroPlayer's OpenGL support is well underway, with foundational work completed and key APIs in place. The remaining tasks include finalizing VAO updates, completing FBO renderbuffers, addressing synchronization challenges, and implementing dynamic resolution support. With these enhancements, RetroPlayer will achieve robust OpenGL integration, paving the way for future advancements like Vulkan support and broader hardware compatibility.
12 changes: 12 additions & 0 deletions xbmc/cores/RetroPlayer/RetroPlayerTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,17 @@ enum class DataAlignment
DATA_UNALIGNED,
DATA_ALIGNED,
};

/*!
* \brief A function pointer representing a hardware procedure
*
* This type alias is used to dynamically load and invoke hardware-specific
* procedures, such as OpenGL or OpenGL ES functions, at runtime. The function
* pointer can be retrieved using the `GetHwProcedureAddress` method in
* \ref CRPProcessInfo.
*
* \note The function must be cast to the appropriate signature before use
*/
using HwProcedureAddress = void (*)();
} // namespace RETRO
} // namespace KODI
1 change: 1 addition & 0 deletions xbmc/cores/RetroPlayer/buffers/BaseRenderBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class CBaseRenderBuffer : public IRenderBuffer
IRenderBufferPool* GetPool() override { return m_pool.get(); }
DataAccess GetMemoryAccess() const override;
DataAlignment GetMemoryAlignment() const override;
uintptr_t GetCurrentFramebuffer() override { return 0; }

protected:
// Reference counting
Expand Down
1 change: 1 addition & 0 deletions xbmc/cores/RetroPlayer/buffers/IRenderBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class IRenderBuffer
virtual size_t GetFrameSize() const = 0;
virtual uint8_t* GetMemory() = 0;
virtual void ReleaseMemory() {}
virtual uintptr_t GetCurrentFramebuffer() = 0;
virtual bool UploadTexture() = 0;
virtual void BindToUnit(unsigned int unit) {}
virtual void SetHeader(void* header) {}
Expand Down
5 changes: 5 additions & 0 deletions xbmc/cores/RetroPlayer/process/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@ set(SOURCES RPProcessInfo.cpp
set(HEADERS RPProcessInfo.h
)

if(TARGET ${APP_NAME_LC}::EGL)
list(APPEND SOURCES egl/RPProcessInfoEGL.cpp)
list(APPEND HEADERS egl/RPProcessInfoEGL.h)
endif()

core_add_library(rp-process)
9 changes: 9 additions & 0 deletions xbmc/cores/RetroPlayer/process/RPProcessInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,15 @@ class CRPProcessInfo
*/
SCALINGMETHOD GetDefaultScalingMethod() const { return m_defaultScalingMethod; }

/*!
* \brief Get a symbol from the hardware context
*
* \param symbol The symbol's name
*
* \return A function pointer for the specified symbol, or nullptr if
* unavailable
*/
virtual HwProcedureAddress GetHwProcedureAddress(const char* symbol) { return nullptr; }
///}

/// @name Player video info
Expand Down
24 changes: 24 additions & 0 deletions xbmc/cores/RetroPlayer/process/egl/RPProcessInfoEGL.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2017-2024 Team Kodi
* This file is part of Kodi - https://kodi.tv
*
* SPDX-License-Identifier: GPL-2.0-or-later
* See LICENSES/README.md for more information.
*/

#include "RPProcessInfoEGL.h"

#include <EGL/egl.h>

using namespace KODI;
using namespace RETRO;

CRPProcessInfoEGL::CRPProcessInfoEGL(std::string platformName)
: CRPProcessInfo(std::move(platformName))
{
}

HwProcedureAddress CRPProcessInfoEGL::GetHwProcedureAddress(const char* symbol)
{
return static_cast<HwProcedureAddress>(eglGetProcAddress(symbol));
}
26 changes: 26 additions & 0 deletions xbmc/cores/RetroPlayer/process/egl/RPProcessInfoEGL.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2017-2024 Team Kodi
* This file is part of Kodi - https://kodi.tv
*
* SPDX-License-Identifier: GPL-2.0-or-later
* See LICENSES/README.md for more information.
*/
#pragma once

#include "cores/RetroPlayer/process/RPProcessInfo.h"

namespace KODI
{
namespace RETRO
{
class CRPProcessInfoEGL : public CRPProcessInfo
{
public:
CRPProcessInfoEGL(std::string platformName);
~CRPProcessInfoEGL() override = default;

// Implementation of CRPProcessInfo
HwProcedureAddress GetHwProcedureAddress(const char* symbol) override;
};
} // namespace RETRO
} // namespace KODI
2 changes: 1 addition & 1 deletion xbmc/cores/RetroPlayer/process/gbm/RPProcessInfoGbm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
using namespace KODI;
using namespace RETRO;

CRPProcessInfoGbm::CRPProcessInfoGbm() : CRPProcessInfo("GBM")
CRPProcessInfoGbm::CRPProcessInfoGbm() : CRPProcessInfoEGL("GBM")
{
}

Expand Down
5 changes: 3 additions & 2 deletions xbmc/cores/RetroPlayer/process/gbm/RPProcessInfoGbm.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@

#pragma once

#include "cores/RetroPlayer/process/RPProcessInfo.h"
#include "cores/RetroPlayer/process/egl/RPProcessInfoEGL.h"

namespace KODI
{
namespace RETRO
{
class CRPProcessInfoGbm : public CRPProcessInfo
class CRPProcessInfoGbm : public CRPProcessInfoEGL
{
public:
CRPProcessInfoGbm();
~CRPProcessInfoGbm() override = default;

static std::unique_ptr<CRPProcessInfo> Create();
static void Register();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
using namespace KODI;
using namespace RETRO;

CRPProcessInfoWayland::CRPProcessInfoWayland() : CRPProcessInfo("Wayland")
CRPProcessInfoWayland::CRPProcessInfoWayland() : CRPProcessInfoEGL("Wayland")
{
}

Expand Down
5 changes: 3 additions & 2 deletions xbmc/cores/RetroPlayer/process/wayland/RPProcessInfoWayland.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@

#pragma once

#include "cores/RetroPlayer/process/RPProcessInfo.h"
#include "cores/RetroPlayer/process/egl/RPProcessInfoEGL.h"

namespace KODI
{
namespace RETRO
{
class CRPProcessInfoWayland : public CRPProcessInfo
class CRPProcessInfoWayland : public CRPProcessInfoEGL
{
public:
CRPProcessInfoWayland();
~CRPProcessInfoWayland() override = default;

static std::unique_ptr<CRPProcessInfo> Create();
static void Register();
Expand Down
29 changes: 29 additions & 0 deletions xbmc/cores/RetroPlayer/rendering/RPRenderManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,35 @@ void CRPRenderManager::AddFrame(const uint8_t* data,
}
}

bool CRPRenderManager::Create(unsigned int width, unsigned int height)
{
//! @todo
return false;
}

uintptr_t CRPRenderManager::GetCurrentFramebuffer(unsigned int width, unsigned int height)
{
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);
return renderBuffer->GetCurrentFramebuffer();
}
}

return 0;
}

void CRPRenderManager::RenderFrame()
{
//! @todo
}

void CRPRenderManager::SetSpeed(double speed)
{
m_speed = speed;
Expand Down
8 changes: 8 additions & 0 deletions xbmc/cores/RetroPlayer/rendering/RPRenderManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ class CRPRenderManager : public IRenderManager, public IRenderCallback
unsigned int orientationDegCW);
void Flush();

// Hardware rendering functions
//! @todo These are only examples pulled from the history of the OpenGL
//! effort and the required redesign will probably remove or change these
//! functions.
bool Create(unsigned int width, unsigned int height);
uintptr_t GetCurrentFramebuffer(unsigned int width, unsigned int height);
void RenderFrame();

// Functions called from the player
void SetSpeed(double speed);

Expand Down
2 changes: 2 additions & 0 deletions xbmc/cores/RetroPlayer/streams/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
set(SOURCES RetroPlayerAudio.cpp
RetroPlayerRendering.cpp
RetroPlayerStreamTypes.cpp
RetroPlayerVideo.cpp
RPStreamManager.cpp
Expand All @@ -7,6 +8,7 @@ set(SOURCES RetroPlayerAudio.cpp
set(HEADERS IRetroPlayerStream.h
IStreamManager.h
RetroPlayerAudio.h
RetroPlayerRendering.h
RetroPlayerStreamTypes.h
RetroPlayerVideo.h
RPStreamManager.h
Expand Down
Loading

0 comments on commit f4dd6df

Please sign in to comment.