112 if (m_graphicsApi == QRhi::Null)
115 QRhi::Flags rhiFlags = {};
118 if (m_graphicsApi == QRhi::OpenGLES2) {
119 m_fallbackSurface.reset(QRhiGles2InitParams::newFallbackSurface(q->format()));
120 QRhiGles2InitParams params;
121 params.fallbackSurface = m_fallbackSurface.get();
123 params.format = q->format();
124 m_rhi.reset(QRhi::create(QRhi::OpenGLES2, ¶ms, rhiFlags));
129 if (m_graphicsApi == QRhi::Vulkan) {
130 QRhiVulkanInitParams params;
131 params.inst = q->vulkanInstance();
133 m_rhi.reset(QRhi::create(QRhi::Vulkan, ¶ms, rhiFlags));
138 if (m_graphicsApi == QRhi::D3D11) {
139 QRhiD3D11InitParams params;
140 params.enableDebugLayer =
true;
141 m_rhi.reset(QRhi::create(QRhi::D3D11, ¶ms, rhiFlags));
146 if (m_graphicsApi == QRhi::Metal) {
147 QRhiMetalInitParams params;
148 m_rhi.reset(QRhi::create(QRhi::Metal, ¶ms, rhiFlags));
154 m_swapChain.reset(m_rhi->newSwapChain());
155 m_swapChain->setWindow(q);
156 m_renderPass.reset(m_swapChain->newCompatibleRenderPassDescriptor());
157 m_swapChain->setRenderPassDescriptor(m_renderPass.get());
159 m_vertexBuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer,
sizeof(g_vw_quad)));
160 m_vertexBuf->create();
163 m_uniformBuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer,
sizeof(QVideoTextureHelper::UniformData)));
164 m_uniformBuf->create();
166 m_textureSampler.reset(m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
167 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
168 m_textureSampler->create();
170 m_shaderResourceBindings.reset(m_rhi->newShaderResourceBindings());
171 m_subtitleResourceBindings.reset(m_rhi->newShaderResourceBindings());
173 m_subtitleUniformBuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer,
sizeof(QVideoTextureHelper::UniformData)));
174 m_subtitleUniformBuf->create();
177void QVideoWindowPrivate::setupGraphicsPipeline(QRhiGraphicsPipeline *pipeline, QRhiShaderResourceBindings *bindings,
const QVideoFrameFormat &fmt)
180 pipeline->setTopology(QRhiGraphicsPipeline::TriangleStrip);
181 QShader vs = vwGetShader(QVideoTextureHelper::vertexShaderFileName(fmt));
182 Q_ASSERT(vs.isValid());
183 QShader fs = vwGetShader(QVideoTextureHelper::fragmentShaderFileName(
184 fmt, m_rhi.get(), m_swapChain->format()));
185 Q_ASSERT(fs.isValid());
186 pipeline->setShaderStages({
187 { QRhiShaderStage::Vertex, vs },
188 { QRhiShaderStage::Fragment, fs }
190 QRhiVertexInputLayout inputLayout;
191 inputLayout.setBindings({
192 { 4 *
sizeof(
float) }
194 inputLayout.setAttributes({
195 { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
196 { 0, 1, QRhiVertexInputAttribute::Float2, 2 *
sizeof(
float) }
198 pipeline->setVertexInputLayout(inputLayout);
199 pipeline->setShaderResourceBindings(bindings);
200 pipeline->setRenderPassDescriptor(m_renderPass.get());
207 if (!m_texturePool.currentFrame().isValid())
208 m_texturePool.setCurrentFrame(QVideoFramePrivate::createFrame(
209 std::make_unique<QMemoryVideoBuffer>(QByteArray{ 4, 0 }, 4),
210 QVideoFrameFormat(QSize(1, 1), QVideoFrameFormat::Format_RGBA8888)));
212 if (!m_texturePool.texturesDirty())
215 QVideoFrameTextures *textures = m_texturePool.updateTextures(*m_rhi, *rub);
221 *(b++) = QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
224 auto fmt = m_texturePool.currentFrame().surfaceFormat();
225 auto textureDesc = QVideoTextureHelper::textureDescription(fmt.pixelFormat());
227 for (
int i = 0; i < textureDesc->nplanes; ++i)
228 (*b++) = QRhiShaderResourceBinding::sampledTexture(
229 i + 1, QRhiShaderResourceBinding::FragmentStage, textures->texture(i),
230 m_textureSampler.get());
231 m_shaderResourceBindings->setBindings(bindings, b);
232 m_shaderResourceBindings->create();
236 if (!m_graphicsPipeline)
237 m_graphicsPipeline.reset(m_rhi->newGraphicsPipeline());
239 setupGraphicsPipeline(m_graphicsPipeline.get(), m_shaderResourceBindings.get(), format);
246 m_hasSubtitle = !m_texturePool.currentFrame().subtitleText().isEmpty();
250 m_subtitleLayout.update(frameSize, m_texturePool.currentFrame().subtitleText());
251 QSize size = m_subtitleLayout.bounds.size().toSize();
253 QImage img = m_subtitleLayout.toImage();
255 m_subtitleTexture.reset(m_rhi->newTexture(QRhiTexture::RGBA8, size));
256 m_subtitleTexture->create();
257 rub->uploadTexture(m_subtitleTexture.get(), img);
261 bindings[0] = QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
262 m_subtitleUniformBuf.get());
264 bindings[1] = QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage,
265 m_subtitleTexture.get(), m_textureSampler.get());
266 m_subtitleResourceBindings->setBindings(bindings, bindings + 2);
267 m_subtitleResourceBindings->create();
269 if (!m_subtitlePipeline) {
270 m_subtitlePipeline.reset(m_rhi->newGraphicsPipeline());
272 QRhiGraphicsPipeline::TargetBlend blend;
274 m_subtitlePipeline->setTargetBlends({ blend });
275 setupGraphicsPipeline(m_subtitlePipeline.get(), m_subtitleResourceBindings.get(), QVideoFrameFormat(QSize(1, 1), QVideoFrameFormat::Format_RGBA8888));
314 QRect rect(0, 0, q->width(), q->height());
317 if (backingStore->size() != q->size())
318 backingStore->resize(q->size());
320 backingStore->beginPaint(rect);
322 QPaintDevice *device = backingStore->paintDevice();
328 frame.paint(&painter, rect, { Qt::black, aspectRatioMode });
331 backingStore->endPaint();
332 backingStore->flush(rect);
336 const VideoTransformation frameTransformation =
337 qNormalizedFrameTransformation(m_texturePool.currentFrame().surfaceFormat());
338 const QSize frameSize = qRotatedFramePresentationSize(m_texturePool.currentFrame());
339 const QSize scaled = frameSize.scaled(rect.size(), aspectRatioMode);
340 QRect videoRect = QRect(QPoint(0, 0), scaled);
341 videoRect.moveCenter(rect.center());
342 QRect subtitleRect = videoRect.intersected(rect);
344 if (!m_hasSwapChain || (m_swapChain->currentPixelSize() != m_swapChain->surfacePixelSize()))
347 const auto requiredSwapChainFormat =
348 qGetRequiredSwapChainFormat(m_texturePool.currentFrame().surfaceFormat());
349 if (qShouldUpdateSwapChainFormat(m_swapChain.get(), requiredSwapChainFormat)) {
351 m_swapChain->setFormat(requiredSwapChainFormat);
358 QRhi::FrameOpResult r = m_rhi->beginFrame(m_swapChain.get());
360 if (r == QRhi::FrameOpSwapChainOutOfDate) {
364 r = m_rhi->beginFrame(m_swapChain.get());
366 if (r != QRhi::FrameOpSuccess) {
367 qWarning(
"beginFrame failed with %d, retry", r);
372 QRhiResourceUpdateBatch *rub = m_rhi->nextResourceUpdateBatch();
376 rub->uploadStaticBuffer(m_vertexBuf.get(), g_vw_quad);
381 if (m_subtitleDirty || m_subtitleLayout.videoSize != subtitleRect.size())
384 const float mirrorFrame = frameTransformation.mirroredHorizontallyAfterRotation ? -1.f : 1.f;
385 const float xscale = mirrorFrame *
float(videoRect.width()) /
float(rect.width());
386 const float yscale = -1.f *
float(videoRect.height()) /
float(rect.height());
388 QMatrix4x4 transform;
389 transform.scale(xscale, yscale);
392 if (m_swapChain->format() == QRhiSwapChain::HDRExtendedSrgbLinear) {
393 auto info = m_swapChain->hdrInfo();
395 maxNits = 100 * info.limits.colorComponentValue.maxColorComponentValue;
397 maxNits = info.limits.luminanceInNits.maxLuminance;
400 QByteArray uniformData;
401 QVideoTextureHelper::updateUniformData(&uniformData, m_rhi.get(),
402 m_texturePool.currentFrame().surfaceFormat(),
403 m_texturePool.currentFrame(), transform, 1.f, maxNits);
404 rub->updateDynamicBuffer(m_uniformBuf.get(), 0, uniformData.size(), uniformData.constData());
408 st.translate(0, -2.f * (
float(m_subtitleLayout.bounds.center().y()) +
float(subtitleRect.top()))/
float(rect.height()) + 1.f);
409 st.scale(
float(m_subtitleLayout.bounds.width())/
float(rect.width()),
410 -1.f *
float(m_subtitleLayout.bounds.height())/
float(rect.height()));
412 QByteArray uniformData;
413 QVideoFrameFormat fmt(m_subtitleLayout.bounds.size().toSize(), QVideoFrameFormat::Format_ARGB8888);
414 QVideoTextureHelper::updateUniformData(&uniformData, m_rhi.get(), fmt, QVideoFrame(), st,
416 rub->updateDynamicBuffer(m_subtitleUniformBuf.get(), 0, uniformData.size(), uniformData.constData());
419 QRhiCommandBuffer *cb = m_swapChain->currentFrameCommandBuffer();
420 cb->beginPass(m_swapChain->currentFrameRenderTarget(), Qt::black, { 1.0f, 0 }, rub);
421 cb->setGraphicsPipeline(m_graphicsPipeline.get());
422 auto size = m_swapChain->currentPixelSize();
423 cb->setViewport({ 0, 0,
float(size.width()),
float(size.height()) });
424 cb->setShaderResources(m_shaderResourceBindings.get());
426 const quint32 vertexOffset = quint32(
sizeof(
float)) * 16 * frameTransformation.rotationIndex();
427 const QRhiCommandBuffer::VertexInput vbufBinding(m_vertexBuf.get(), vertexOffset);
428 cb->setVertexInput(0, 1, &vbufBinding);
432 cb->setGraphicsPipeline(m_subtitlePipeline.get());
433 cb->setShaderResources(m_subtitleResourceBindings.get());
434 const QRhiCommandBuffer::VertexInput vbufBinding(m_vertexBuf.get(), 0);
435 cb->setVertexInput(0, 1, &vbufBinding);
441 m_rhi->endFrame(m_swapChain.get());
443 m_texturePool.onFrameEndInvoked();