PonyPlayer
renderer.hpp
浏览该文件的文档.
1//
2// Created by ColorsWind on 2022/5/7.
3//
4#pragma once
5#ifdef __APPLE__
6// suppress apple warning
7#define GL_SILENCE_DEPRECATION
8#include <OpenGL/gl.h>
9#undef GL_SILENCE_DEPRECATION
10#endif
11
12#include <QQuickItem>
13#include <QQuickWindow>
14#include <QOpenGLShaderProgram>
15#include <QOpenGLFunctions>
16#include <QOpenGLVertexArrayObject>
17#include <QOpenGLTexture>
18#include <QOpenGLBuffer>
19#include <QQuickFramebufferObject>
20#include <QOpenGLFunctions_3_3_Core>
21#include <QOpenGLDebugLogger>
22#include <QVector>
23#include <QSGSimpleTextureNode>
24#include <QSGRenderNode>
25#include <utility>
26#include "updatevalue.hpp"
27#include "frame.hpp"
28
29const static void* ZERO_OFFSET = nullptr;
30const static GLfloat VERTEX_POS[] = {
31 1, 1, 0, 1, 0,
32 1, 0, 0, 1, 1,
33 0, 1, 0, 0, 0,
34 0, 0, 0, 0, 1,
35};
36const static GLuint VERTEX_INDEX[] = {
37 0, 1, 2,
38 1, 2, 3,
39};
40
42 // For memory visibility, sync when both GUI and render threads are blocked.
43 // We have to RenderSettings, one can be modified on GUI thread (e.g. by QML),
44 // and the other will be synchronized in SYNC stage.
51
52 void updateBy(RenderSettings &settings) {
54 brightness.updateBy(settings.brightness);
55 contrast.updateBy(settings.contrast);
56 saturation.updateBy(settings.saturation);
57 lutFilter.updateBy(settings.lutFilter);
58 keepFrameRate.updateBy(settings.keepFrameRate);
59 }
60
61};
62
63
64// QuickItem lives in the GUI thread and the rendering potentially happens on the render thread.
65// QuickItem may be deleted on the GUI thread while the render thread is rendering.
66// Therefore, we need to separate those two objects.
67class FireworksRenderer : public QObject, public QSGRenderNode, protected QOpenGLFunctions_3_3_Core {
68 Q_OBJECT
69 Q_PROPERTY(GLfloat brightness READ getBrightness WRITE setBrightness)
70 Q_PROPERTY(GLfloat contrast READ getContrast WRITE setContrast)
71 Q_PROPERTY(GLfloat saturation READ getSaturation WRITE setSaturation)
72 Q_PROPERTY(bool keepFrameRate READ isKeepFrameRate WRITE setKeepFrameRate)
73 friend class Fireworks;
74
75PONY_GUARD_BY(MAIN) private:
76 // properties
77
78 [[nodiscard]] bool isKeepFrameRate() const { return mainSettings.keepFrameRate; }
79
80 void setKeepFrameRate(bool keep) { mainSettings.keepFrameRate = keep; }
81
82 [[nodiscard]] GLfloat getBrightness() const { return mainSettings.brightness; }
83
84 void setBrightness(GLfloat brightness) { mainSettings.brightness = brightness; }
85
86 [[nodiscard]] GLfloat getSaturation() const { return mainSettings.saturation; }
87
88 void setSaturation(GLfloat saturation) { mainSettings.saturation = saturation; }
89
90 [[nodiscard]] GLfloat getContrast() const { return mainSettings.contrast; }
91
92 void setContrast(GLfloat contrast) { mainSettings.contrast = contrast; }
93
94
95private:
96 QOpenGLShaderProgram *program = nullptr; // late init
97 QMatrix4x4 viewMatrix;
98
99 PONY_GUARD_BY(MAIN) RenderSettings mainSettings;
100 PONY_GUARD_BY(RENDER) RenderSettings renderSettings;
101
102 void inline createTextureBuffer(GLuint *texture) {
103 QOpenGLFunctions_3_3_Core::glGenTextures(1, texture);
104 QOpenGLFunctions_3_3_Core::glBindTexture(GL_TEXTURE_2D, *texture);
105 QOpenGLFunctions_3_3_Core::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
106 QOpenGLFunctions_3_3_Core::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
107 QOpenGLFunctions_3_3_Core::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
108 QOpenGLFunctions_3_3_Core::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
109 }
110
111private:
112 // opengl location
113 GLuint vao = 0, vbo = 0, ebo = 0;
114 GLuint textureY = 0, textureU = 0, textureV = 0, textureLUT = 0;
115 GLint brightnessLoc = -1, contrastLoc = -1, saturationLoc = -1;
116public slots:
117 void init() {
118 if (program) { return; } // already initialized
119 program = new QOpenGLShaderProgram;
120 QOpenGLFunctions_3_3_Core::initializeOpenGLFunctions();
121 static bool info = false;
122 if (!info) {
123 qInfo() << "OpenGL version:"
124 << QLatin1String(reinterpret_cast<const char*>(glGetString(GL_VERSION)));
125 qInfo() << "GSLS version:"
126 << QLatin1String(reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION)));
127 qInfo() << "Vendor:" << QLatin1String(reinterpret_cast<const char*>(glGetString(GL_VENDOR)));
128 qInfo() << "Renderer:" << QLatin1String(reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
129 info = true;
130 }
131 program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, u":/player/shader/vertex.vsh"_qs);
132 program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, u":/player/shader/fragment.fsh"_qs);
133 program->link();
134 program->bind();
135 brightnessLoc = program->uniformLocation("brightness");
136 contrastLoc = program->uniformLocation("contrast");
137 saturationLoc = program->uniformLocation("saturation");
138
139// glClearColor(0.1f, 0.1f, 0.3f, 1.0f);
140 glGenVertexArrays(1, &vao);
141 glGenBuffers(1, &vbo);
142 glGenBuffers(1, &ebo);
143
144 glBindBuffer(GL_ARRAY_BUFFER, vbo);
145 glBufferData(GL_ARRAY_BUFFER, sizeof(VERTEX_POS), VERTEX_POS, GL_STATIC_DRAW);
146
147 glBindVertexArray(vao);
148 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), ZERO_OFFSET);
149 glEnableVertexAttribArray(0);
150 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat),
151 reinterpret_cast<const void *>(3 * sizeof(GLfloat)));
152 glEnableVertexAttribArray(1);
153
154 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
155 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(VERTEX_INDEX), VERTEX_INDEX, GL_STATIC_DRAW);
156
157 program->setUniformValue("tex_y", 0);
158 program->setUniformValue("tex_u", 1);
159 program->setUniformValue("tex_v", 2);
160 program->setUniformValue("tex_lut", 3);
161 glEnable(GL_TEXTURE_2D);
162 glActiveTexture(GL_TEXTURE0);
163 createTextureBuffer(&textureY);
164 glActiveTexture(GL_TEXTURE1);
165 createTextureBuffer(&textureU);
166 glActiveTexture(GL_TEXTURE2);
167 createTextureBuffer(&textureV);
168 glActiveTexture(GL_TEXTURE3);
169 createTextureBuffer(&textureLUT);
170 glBindTexture(GL_TEXTURE_2D, textureLUT);
171 };
172
173 void sync() {
174 renderSettings.updateBy(mainSettings);
175 }
176public:
178 qDebug() << "Create Hurricane Renderer:" << static_cast<void *>(this) << ".";
179 }
180
181 PONY_GUARD_BY(MAIN) bool setVideoFrame(const VideoFrameRef &frame) {
182 if (static_cast<const VideoFrameRef&>(mainSettings.videoFrame) == frame) {
183 return false;
184 } {
185 mainSettings.videoFrame = frame;
186 return true;
187 }
188 }
189
190 PONY_GUARD_BY(MAIN) void setLUTFilter(const QImage& image) {
191 mainSettings.lutFilter = image;
192 }
193
194
195public:
196
197 [[nodiscard]] RenderingFlags flags() const override {
198 return BoundedRectRendering;
199 }
200
201 [[nodiscard]] StateFlags changedStates() const override {
202 return BlendState | ScissorState | StencilState;
203 }
204
205 void render(const RenderState *state) override {
206 // call on render thread
207
208 // Due to QTBUG-97589, we are not able to get model-view matrix
209 // https://bugreports.qt.io/browse/QTBUG-97589
210 // workaround, assume parent clip hurricane
211 const QRect r = state->scissorRect();
212
213 glEnable(GL_BLEND);
214 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
215 if (state->scissorEnabled()) {
216 glEnable(GL_SCISSOR_TEST);
217 glScissor(r.x(), r.y(), r.width(), r.height());
218 } else {
219 ILLEGAL_STATE("Scissor Test must be enabled. For example: wrap Fireworks "
220 "in a Rectangle and set clip: true. ");
221 }
222 if (state->stencilEnabled()) {
223 glEnable(GL_STENCIL_TEST);
224 glStencilFunc(GL_EQUAL, state->stencilValue(), 0xFF);
225 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
226 }
227
228
229 program->bind();
230
231 program->setUniformValue(saturationLoc, renderSettings.saturation.getUpdate());
232 program->setUniformValue(contrastLoc, renderSettings.contrast.getUpdate());
233 program->setUniformValue(brightnessLoc, renderSettings.brightness.getUpdate());
234 glBindVertexArray(vao);
235 glBindBuffer(GL_ARRAY_BUFFER, vbo);
236 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
237 const VideoFrameRef &videoFrame = renderSettings.videoFrame;
238 if (!videoFrame.isValid()) { return; }
239 int lineSize = videoFrame.getLineSize();
240 int imageHeight = videoFrame.getHeight();
241 int imageWidth = videoFrame.getWidth();
242 auto *imageY = videoFrame.getY();
243 auto *imageU = videoFrame.getU();
244 auto *imageV = videoFrame.getV();
245 if (renderSettings.keepFrameRate) {
246 double rate = static_cast<double>(imageHeight) / static_cast<double>(imageWidth);
247 double h = r.width() * rate;
248 if (h >= r.height()) {
249 double wFit = r.height() / rate;
250 double wPad = r.width() - wFit;
251 glViewport(r.x() + static_cast<GLsizei>(wPad) / 2, r.y(), static_cast<GLsizei>(wFit), r.height());
252 } else {
253 double hFit = r.width() * rate;
254 double hPad = r.height() - hFit;
255 glViewport(r.x(), r.y() + static_cast<GLsizei>(hPad) / 2, r.width(), static_cast<GLsizei>(hFit));
256 }
257 } else {
258 glViewport(r.x(), r.y(), r.width(), r.height());
259 }
260
261
262 QImage lutTexture = renderSettings.lutFilter;
263
264 if (renderSettings.videoFrame.isUpdateSize()) {
265 // since FFmpeg may pad frame to align, we need to clip invalid data
266 viewMatrix.setToIdentity();
267 viewMatrix.ortho(0, static_cast<float>(imageWidth) / static_cast<float>(lineSize), 0, 1, -1, 1);
268// viewMatrix.translate(0, 0, -0.7F);
269 program->setUniformValue("view", viewMatrix);
270 glActiveTexture(GL_TEXTURE0);
271 glBindTexture(GL_TEXTURE_2D, textureY);
272 glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, lineSize, imageHeight, 0, GL_RED, GL_UNSIGNED_BYTE, imageY);
273 glActiveTexture(GL_TEXTURE1);
274 glBindTexture(GL_TEXTURE_2D, textureU);
275 glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, lineSize / 2, imageHeight / 2, 0, GL_RED, GL_UNSIGNED_BYTE, imageU);
276 glActiveTexture(GL_TEXTURE2);
277 glBindTexture(GL_TEXTURE_2D, textureV);
278 glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, lineSize / 2, imageHeight / 2, 0, GL_RED, GL_UNSIGNED_BYTE, imageV);
279 glActiveTexture(GL_TEXTURE3);
280 glBindTexture(GL_TEXTURE_2D, textureLUT);
281 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, lutTexture.width(), lutTexture.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, lutTexture.constBits());
282 } else if (renderSettings.videoFrame.isUpdate()) {
283 glActiveTexture(GL_TEXTURE0);
284 glBindTexture(GL_TEXTURE_2D, textureY);
285 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, lineSize, imageHeight, GL_RED, GL_UNSIGNED_BYTE, imageY);
286 glActiveTexture(GL_TEXTURE1);
287 glBindTexture(GL_TEXTURE_2D, textureU);
288 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, lineSize / 2, imageHeight / 2, GL_RED, GL_UNSIGNED_BYTE, imageU);
289 glActiveTexture(GL_TEXTURE2);
290 glBindTexture(GL_TEXTURE_2D, textureV);
291 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, lineSize / 2, imageHeight / 2, GL_RED, GL_UNSIGNED_BYTE, imageV);
292 glBindTexture(GL_TEXTURE_2D, textureLUT);
293 glActiveTexture(GL_TEXTURE3);
294 glBindTexture(GL_TEXTURE_2D, textureLUT);
295 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, lutTexture.width(), lutTexture.height(), GL_RED, GL_UNSIGNED_BYTE, lutTexture.constBits());
296 }
297 glDrawElements(GL_TRIANGLES, sizeof(VERTEX_INDEX) / sizeof(GLuint), GL_UNSIGNED_INT, ZERO_OFFSET);
298 program->release();
299 }
300
302 glBindBuffer(GL_ARRAY_BUFFER, 0);
303 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
304 glBindVertexArray(0);
305 program->release();
306 qDebug() << "Deconstruct Hurricane Renderer:" << static_cast<void *>(this) << ".";
307 }
308
309
310};
Definition: fireworks.hpp:10
QML_ELEMENTbool keepFrameRate
Definition: fireworks.hpp:13
Definition: renderer.hpp:67
void render(const RenderState *state) override
Definition: renderer.hpp:205
GLfloat brightness
Definition: renderer.hpp:69
PONY_GUARD_BY(MAIN) bool setVideoFrame(const VideoFrameRef &frame)
Definition: renderer.hpp:181
PONY_GUARD_BY(MAIN) void setLUTFilter(const QImage &image)
Definition: renderer.hpp:190
PONY_GUARD_BY(MAIN) FireworksRenderer()
Definition: renderer.hpp:177
void sync()
Definition: renderer.hpp:173
void init()
Definition: renderer.hpp:117
bool keepFrameRate
Definition: renderer.hpp:72
GLfloat saturation
Definition: renderer.hpp:71
RenderingFlags flags() const override
Definition: renderer.hpp:197
~FireworksRenderer() override
Definition: renderer.hpp:301
StateFlags changedStates() const override
Definition: renderer.hpp:201
GLfloat contrast
Definition: renderer.hpp:70
Definition: updatevalue.hpp:9
bool isUpdate() const
Definition: updatevalue.hpp:39
Definition: updatevalue.hpp:44
void updateBy(UpdateValueVideoFrameRef &updateValue)
Definition: updatevalue.hpp:68
bool isUpdateSize() const
Definition: updatevalue.hpp:74
Definition: frame.hpp:47
std::byte * getV() const
Definition: frame.hpp:114
bool isValid() const
Definition: frame.hpp:97
int getWidth() const
Definition: frame.hpp:122
int getLineSize() const
Definition: frame.hpp:118
std::byte * getU() const
Definition: frame.hpp:110
std::byte * getY() const
Definition: frame.hpp:106
int getHeight() const
Definition: frame.hpp:126
constexpr PonyThread MAIN
Definition: ponyplayer.h:47
constexpr PonyThread RENDER
Definition: ponyplayer.h:48
#define ILLEGAL_STATE(msg)
Definition: ponyplayer.h:40
Definition: renderer.hpp:41
void updateBy(RenderSettings &settings)
Definition: renderer.hpp:52
UpdateValue< GLfloat > brightness
Definition: renderer.hpp:46
UpdateValue< GLfloat > contrast
Definition: renderer.hpp:47
UpdateValue< GLfloat > saturation
Definition: renderer.hpp:48
UpdateValueVideoFrameRef videoFrame
Definition: renderer.hpp:45
UpdateValue< bool > keepFrameRate
Definition: renderer.hpp:50
UpdateValue< QImage > lutFilter
Definition: renderer.hpp:49