1 /* 2 * Copyright (C) 2017-2018 Team Kodi 3 * This file is part of Kodi - https://kodi.tv 4 * 5 * SPDX-License-Identifier: GPL-2.0-or-later 6 * See LICENSES/README.md for more information. 7 */ 8 9 #pragma once 10 11 #include "IRenderManager.h" 12 #include "RenderVideoSettings.h" 13 #include "cores/RetroPlayer/guibridge/IRenderCallback.h" 14 #include "threads/CriticalSection.h" 15 16 extern "C" 17 { 18 #include <libavutil/pixfmt.h> 19 } 20 21 #include <atomic> 22 #include <map> 23 #include <memory> 24 #include <set> 25 #include <vector> 26 27 struct SwsContext; 28 29 namespace KODI 30 { 31 namespace RETRO 32 { 33 class CGUIRenderTargetFactory; 34 class CRenderContext; 35 class CRenderSettings; 36 class CRPBaseRenderer; 37 class CRPProcessInfo; 38 class IGUIRenderSettings; 39 class IRenderBuffer; 40 class IRenderBufferPool; 41 42 /*! 43 * \brief Renders video frames provided by the game loop 44 * 45 * Generally, buffer pools are registered by the windowing subsystem. A buffer 46 * pool provides a software or hardware buffer to store the added frame. When 47 * RenderManager is created, it instantiates all registered buffer pools. 48 * 49 * When a frame arrives, it is copied into a buffer from each buffer pool with 50 * a visible renderer. For example, if a GLES and MMAL renderer are both 51 * visible in the GUI, then the frame will be copied into two buffers. 52 * 53 * When it is time to render the frame, the GUI control or window calls into 54 * this class through the IRenderManager interface. RenderManager selects an 55 * appropriate renderer to use to render the frame. The renderer is then 56 * given the buffer that came from its buffer pool. 57 * 58 * Special behavior is needed when the game is paused. As no new frames are 59 * delivered, a newly created renderer will stay black. For this scenario, 60 * when we detect a pause event, the frame is preemptively cached so that a 61 * newly created renderer will have something to display. 62 */ 63 class CRPRenderManager : public IRenderManager, public IRenderCallback 64 { 65 public: 66 CRPRenderManager(CRPProcessInfo& processInfo); 67 ~CRPRenderManager() override = default; 68 69 void Initialize(); 70 void Deinitialize(); 71 72 /*! 73 * \brief Access the factory for creating GUI render targets 74 */ GetGUIRenderTargetFactory()75 CGUIRenderTargetFactory* GetGUIRenderTargetFactory() { return m_renderControlFactory.get(); } 76 77 // Functions called from game loop 78 bool Configure(AVPixelFormat format, 79 unsigned int nominalWidth, 80 unsigned int nominalHeight, 81 unsigned int maxWidth, 82 unsigned int maxHeight); 83 bool GetVideoBuffer( 84 unsigned int width, unsigned int height, AVPixelFormat& format, uint8_t*& data, size_t& size); 85 void AddFrame(const uint8_t* data, 86 size_t size, 87 unsigned int width, 88 unsigned int height, 89 unsigned int orientationDegCW); 90 void Flush(); 91 92 // Functions called from the player 93 void SetSpeed(double speed); 94 95 // Functions called from render thread 96 void FrameMove(); 97 98 // Implementation of IRenderManager 99 void RenderWindow(bool bClear, const RESOLUTION_INFO& coordsRes) override; 100 void RenderControl(bool bClear, 101 bool bUseAlpha, 102 const CRect& renderRegion, 103 const IGUIRenderSettings* renderSettings) override; 104 void ClearBackground() override; 105 106 // Implementation of IRenderCallback 107 bool SupportsRenderFeature(RENDERFEATURE feature) const override; 108 bool SupportsScalingMethod(SCALINGMETHOD method) const override; 109 110 private: 111 /*! 112 * \brief Get or create a renderer compatible with the given render settings 113 */ 114 std::shared_ptr<CRPBaseRenderer> GetRenderer(const IGUIRenderSettings* renderSettings); 115 116 /*! 117 * \brief Get or create a renderer for the given buffer pool and render settings 118 */ 119 std::shared_ptr<CRPBaseRenderer> GetRenderer(IRenderBufferPool* bufferPool, 120 const CRenderSettings& renderSettings); 121 122 /*! 123 * \brief Render a frame using the given renderer 124 */ 125 void RenderInternal(const std::shared_ptr<CRPBaseRenderer>& renderer, 126 IRenderBuffer* renderBuffer, 127 bool bClear, 128 uint32_t alpha); 129 130 /*! 131 * \brief Return true if we have a render buffer belonging to the specified pool 132 */ 133 bool HasRenderBuffer(IRenderBufferPool* bufferPool); 134 135 /*! 136 * \brief Get a render buffer belonging to the specified pool 137 */ 138 IRenderBuffer* GetRenderBuffer(IRenderBufferPool* bufferPool); 139 140 /*! 141 * \brief Create a render buffer for the specified pool from a cached frame 142 */ 143 void CreateRenderBuffer(IRenderBufferPool* bufferPool); 144 145 /*! 146 * \brief Create a render buffer and copy the cached data into it 147 * 148 * The cached frame is accessed by both the game and rendering threads, 149 * and therefore requires synchronization. 150 * 151 * However, assuming the memory copy is expensive, we must avoid holding 152 * the mutex during the copy. 153 * 154 * To allow for this, the function is permitted to invalidate its 155 * cachedFrame parameter, as long as it is restored upon exit. While the 156 * mutex is exited inside this function, cachedFrame is guaranteed to be 157 * empty. 158 * 159 * \param cachedFrame The cached frame 160 * \param width The width of the cached frame 161 * \param height The height of the cached frame 162 * \param bufferPool The buffer pool used to create the render buffer 163 * \param mutex The locked mutex, to be unlocked during memory copy 164 * 165 * \return The render buffer if one was created from the cached frame, 166 * otherwise nullptr 167 */ 168 IRenderBuffer* CreateFromCache(std::vector<uint8_t>& cachedFrame, 169 unsigned int width, 170 unsigned int height, 171 IRenderBufferPool* bufferPool, 172 CCriticalSection& mutex); 173 174 /*! 175 * \brief Utility function to copy a frame and rescale pixels if necessary 176 */ 177 void CopyFrame(IRenderBuffer* renderBuffer, 178 AVPixelFormat format, 179 const uint8_t* data, 180 size_t size, 181 unsigned int width, 182 unsigned int height); 183 184 CRenderVideoSettings GetEffectiveSettings(const IGUIRenderSettings* settings) const; 185 186 void CheckFlush(); 187 188 // Construction parameters 189 CRPProcessInfo& m_processInfo; 190 CRenderContext& m_renderContext; 191 192 // Subsystems 193 std::shared_ptr<IGUIRenderSettings> m_renderSettings; 194 std::shared_ptr<CGUIRenderTargetFactory> m_renderControlFactory; 195 196 // Stream properties 197 AVPixelFormat m_format = AV_PIX_FMT_NONE; 198 unsigned int m_maxWidth = 0; 199 unsigned int m_maxHeight = 0; 200 201 // Render resources 202 std::set<std::shared_ptr<CRPBaseRenderer>> m_renderers; 203 std::vector<IRenderBuffer*> m_pendingBuffers; // Only access from game thread 204 std::vector<IRenderBuffer*> m_renderBuffers; 205 std::map<AVPixelFormat, SwsContext*> m_scalers; 206 std::vector<uint8_t> m_cachedFrame; 207 unsigned int m_cachedWidth = 0; 208 unsigned int m_cachedHeight = 0; 209 210 // State parameters 211 enum class RENDER_STATE 212 { 213 UNCONFIGURED, 214 CONFIGURING, 215 CONFIGURED, 216 }; 217 RENDER_STATE m_state = RENDER_STATE::UNCONFIGURED; 218 bool m_bHasCachedFrame = false; // Invariant: m_cachedFrame is empty if false 219 std::set<std::string> m_failedShaderPresets; 220 std::atomic<bool> m_bFlush = {false}; 221 222 // Windowing state 223 bool m_bDisplayScaleSet = false; 224 225 // Playback parameters 226 std::atomic<double> m_speed = {1.0}; 227 228 // Synchronization parameters 229 CCriticalSection m_stateMutex; 230 CCriticalSection m_bufferMutex; 231 }; 232 } // namespace RETRO 233 } // namespace KODI 234