Overte C++ Documentation
VulkanDisplayPlugin.h
1 //
2 // Created by dr Karol Suprynowicz on 2024/08/12
3 // Based on OpenGLDisplayPlugin.h, originally created by Bradley Austin Davis on 2015/05/29
4 // Copyright 2015 High Fidelity, Inc.
5 // Copyright 2025 Overte e.V.
6 //
7 // Distributed under the Apache License, Version 2.0.
8 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
9 //
10 #pragma once
11 
12 // TODO: find a way of doing this from CMake. I'm not familiar enough with build system to figure this out.
13 #ifndef USE_GL
14 
15 #include "DisplayPlugin.h"
16 
17 #include <condition_variable>
18 #include <memory>
19 #include <queue>
20 
21 #include <QtCore/QThread>
22 #include <QtCore/QTimer>
23 #include <QtGui/QImage>
24 
25 #include <GLMHelpers.h>
26 #include <SimpleMovingAverage.h>
27 #include <shared/RateCounter.h>
28 
29 #include <gpu/Batch.h>
30 #include <vk/VKWindow.h>
31 
32 class RefreshRateController;
33 
34 class VulkanDisplayPlugin : public DisplayPlugin {
35  Q_OBJECT
36  Q_PROPERTY(float hudAlpha MEMBER _hudAlpha)
37  using Parent = DisplayPlugin;
38 
39 protected:
40  using Mutex = std::mutex;
41  using Lock = std::unique_lock<Mutex>;
42  using Condition = std::condition_variable;
43 
44 public:
45  ~VulkanDisplayPlugin();
46  // These must be final to ensure proper ordering of operations
47  // between the main thread and the presentation thread
48 
49  static std::function<void(int)> getRefreshRateOperator();
50 
51  bool activate() override final;
52  void deactivate() override final;
53  bool startStandBySession() override final;
54  void endSession() override final;
55  bool eventFilter(QObject* receiver, QEvent* event) override;
56  bool isDisplayVisible() const override { return true; }
57  bool isSupported() const override;
58  void captureFrame(const std::string& outputName) const override;
59  void submitFrame(const gpu::FramePointer& newFrame) override;
60 
61  glm::uvec2 getRecommendedRenderSize() const override { return getSurfacePixels(); }
62 
63  glm::uvec2 getRecommendedUiSize() const override { return getSurfaceSize(); }
64 
65  virtual bool setDisplayTexture(const QString& name) override;
66  virtual bool onDisplayTextureReset() { return false; };
67 
68  float presentRate() const override;
69 
70  void resetPresentRate() override;
71 
72  float newFramePresentRate() const override;
73 
74  float droppedFrameRate() const override;
75 
76  float renderRate() const override;
77 
78  bool beginFrameRender(uint32_t frameIndex) override;
79 
80  virtual bool wantVsync() const { return true; }
81  void setVsyncEnabled(bool vsyncEnabled) { _vsyncEnabled = vsyncEnabled; }
82  bool isVsyncEnabled() const { return _vsyncEnabled; }
83  // Three threads, one for rendering, one for texture transfers, one reserved for the GL driver
84  // Drop to one reserved for better other-task performance in desktop
85  int getRequiredThreadCount() const override { return 1; }
86 
87  virtual std::function<void(gpu::Batch&, const gpu::TexturePointer&)> getHUDOperator() override;
88  void copyTextureToQuickFramebuffer(NetworkTexturePointer source,
89  QOpenGLFramebufferObject* target,
90  GLsync* fenceSync) override;
91 
92  virtual const QString getName() const override { return "Vulkan window"; }
93  virtual void pluginUpdate() override {};
94 
95 protected:
96  friend class VulkanPresentThread;
97 
98  glm::uvec2 getSurfaceSize() const;
99  glm::uvec2 getSurfacePixels() const;
100  // Some display plugins require us to always execute some present logic,
101  // whether we have a frame or not (Oculus Mobile plugin)
102  // Such plugins must be prepared to do the right thing if the `_currentFrame`
103  // is not populated
104  virtual bool alwaysPresent() const { return false; }
105 
106  void updateCompositeFramebuffer();
107 
108  virtual QThread::Priority getPresentPriority() { return QThread::HighPriority; }
109  virtual void compositeLayers();
110  virtual void setupCompositeScenePipeline(gpu::Batch& batch);
111  virtual void compositeScene();
112  virtual void compositePointer();
113  virtual void compositeExtra(){};
114 
115  // These functions must only be called on the presentation thread
116  virtual void customizeContext();
117  virtual void uncustomizeContext();
118 
119  // Returns true on successful activation
120  virtual bool internalActivate() { return true; }
121  virtual void internalDeactivate() {}
122 
123  // Returns true on successful activation of standby session
124  virtual bool activateStandBySession() { return true; }
125  virtual void deactivateSession() {}
126 
127  // Plugin specific functionality to send the composed scene to the output window or device
128  virtual void internalPresent();
129 
130  void renderFromTexture(gpu::Batch& batch,
131  const gpu::TexturePointer& texture,
132  const glm::ivec4& viewport,
133  const glm::ivec4& scissor,
134  const gpu::FramebufferPointer& fbo);
135  void renderFromTexture(gpu::Batch& batch,
136  const gpu::TexturePointer& texture,
137  const glm::ivec4& viewport,
138  const glm::ivec4& scissor);
139  virtual void updateFrameData();
140  virtual glm::mat4 getViewCorrection() { return glm::mat4(); }
141 
142  void withOtherThreadContext(std::function<void()> f) const;
143 
144  void present(const std::shared_ptr<RefreshRateController>& refreshRateController);
145  virtual void swapBuffers();
146 
147  void render(std::function<void(gpu::Batch& batch)> f);
148 
149  bool _vsyncEnabled{ true };
150  QThread* _presentThread{ nullptr };
151  std::queue<gpu::FramePointer> _newFrameQueue;
152  RateCounter<200> _droppedFrameRate;
153  RateCounter<200> _newFrameRate;
154  RateCounter<200> _presentRate;
155  RateCounter<200> _renderRate;
156 
157  gpu::FramePointer _currentFrame;
158  gpu::Frame* _lastFrame{ nullptr };
159  mat4 _prevRenderView;
160  gpu::FramebufferPointer _compositeFramebuffer;
161  gpu::PipelinePointer _hudPipeline;
162  gpu::PipelinePointer _mirrorHUDPipeline;
163  gpu::ShaderPointer _mirrorHUDPS;
164  gpu::PipelinePointer _drawTexturePipeline;
165  gpu::PipelinePointer _drawTextureSqueezePipeline;
166  gpu::PipelinePointer _linearToSRGBPipeline;
167  gpu::PipelinePointer _SRGBToLinearPipeline;
168  gpu::PipelinePointer _cursorPipeline;
169  gpu::TexturePointer _displayTexture{};
170  float _compositeHUDAlpha{ 1.0f };
171 
172  virtual gpu::PipelinePointer getRenderTexturePipeline();
173 
174  struct CursorData {
175  QImage image;
176  vec2 hotSpot;
177  uvec2 size;
178  gpu::TexturePointer texture;
179  };
180 
181  std::map<uint16_t, CursorData> _cursorsData;
182  bool _lockCurrentTexture{ false };
183 
184  void assertNotPresentThread() const;
185  void assertIsPresentThread() const;
186 
187  template <typename F>
188  void withPresentThreadLock(F f) const {
189  assertIsPresentThread();
190  Lock lock(_presentMutex);
191  f();
192  }
193 
194  template <typename F>
195  void withNonPresentThreadLock(F f) const {
196  assertNotPresentThread();
197  Lock lock(_presentMutex);
198  f();
199  }
200 
201  const gpu::BackendPointer& getBackend() const;
202 
203  // Any resource shared by the main thread and the presentation thread must
204  // be accessed through this mutex
205  mutable Mutex _presentMutex;
206  float _hudAlpha{ 1.0f };
207 
208  QImage getScreenshot(float aspectRatio);
209  QImage getSecondaryCameraScreenshot();
210  //std::shared_ptr<VKWindow> _vkWindow;
211  //VKWidget *_vkWidget{ 0 };
212  VKWindow *_vkWindow{ 0 };
213  int _renderedFrameCount{ 0 };
214 };
215 
216 #endif