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