113void QVideoWindowPrivate::initRhi()
115 if (m_graphicsApi == QRhi::Null)
118 QRhi::Flags rhiFlags = {};
121 if (m_graphicsApi == QRhi::OpenGLES2) {
122 m_fallbackSurface.reset(QRhiGles2InitParams::newFallbackSurface(q->format()));
123 QRhiGles2InitParams params;
124 params.fallbackSurface = m_fallbackSurface.get();
126 params.format = q->format();
127 m_rhi.reset(QRhi::create(QRhi::OpenGLES2, ¶ms, rhiFlags));
132 if (m_graphicsApi == QRhi::Vulkan) {
133 QRhiVulkanInitParams params;
134 params.inst = q->vulkanInstance();
136 m_rhi.reset(QRhi::create(QRhi::Vulkan, ¶ms, rhiFlags));
141 if (m_graphicsApi == QRhi::D3D11) {
142 QRhiD3D11InitParams params;
143 params.enableDebugLayer =
true;
144 m_rhi.reset(QRhi::create(QRhi::D3D11, ¶ms, rhiFlags));
149 if (m_graphicsApi == QRhi::Metal) {
150 QRhiMetalInitParams params;
151 m_rhi.reset(QRhi::create(QRhi::Metal, ¶ms, rhiFlags));
157 m_swapChain.reset(m_rhi->newSwapChain());
158 m_swapChain->setWindow(q);
159 m_renderPass.reset(m_swapChain->newCompatibleRenderPassDescriptor());
160 m_swapChain->setRenderPassDescriptor(m_renderPass.get());
162 m_vertexBuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer,
sizeof(g_vw_quad)));
163 m_vertexBuf->create();
164 m_vertexBufReady =
false;
166 m_uniformBuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer,
sizeof(QVideoTextureHelper::UniformData)));
167 m_uniformBuf->create();
169 m_textureSampler.reset(m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
170 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
171 m_textureSampler->create();
173 m_shaderResourceBindings.reset(m_rhi->newShaderResourceBindings());
174 m_subtitleResourceBindings.reset(m_rhi->newShaderResourceBindings());
176 m_subtitleUniformBuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer,
sizeof(QVideoTextureHelper::UniformData)));
177 m_subtitleUniformBuf->create();
180void QVideoWindowPrivate::setupGraphicsPipeline(QRhiGraphicsPipeline *pipeline, QRhiShaderResourceBindings *bindings,
const QVideoFrameFormat &fmt)
183 pipeline->setTopology(QRhiGraphicsPipeline::TriangleStrip);
184 QShader vs = vwGetShader(QVideoTextureHelper::vertexShaderFileName(fmt));
185 Q_ASSERT(vs.isValid());
186 QShader fs = vwGetShader(QVideoTextureHelper::fragmentShaderFileName(
187 fmt, m_rhi.get(), m_swapChain->format()));
188 Q_ASSERT(fs.isValid());
189 pipeline->setShaderStages({
190 { QRhiShaderStage::Vertex, vs },
191 { QRhiShaderStage::Fragment, fs }
193 QRhiVertexInputLayout inputLayout;
194 inputLayout.setBindings({
195 { 4 *
sizeof(
float) }
197 inputLayout.setAttributes({
198 { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
199 { 0, 1, QRhiVertexInputAttribute::Float2, 2 *
sizeof(
float) }
201 pipeline->setVertexInputLayout(inputLayout);
202 pipeline->setShaderResourceBindings(bindings);
203 pipeline->setRenderPassDescriptor(m_renderPass.get());
207void QVideoWindowPrivate::updateTextures(QRhiResourceUpdateBatch *rub)
210 if (!m_texturePool.currentFrame().isValid())
211 m_texturePool.setCurrentFrame(QVideoFramePrivate::createFrame(
212 std::make_unique<QMemoryVideoBuffer>(QByteArray{ 4, 0 }, 4),
213 QVideoFrameFormat(QSize(1, 1), QVideoFrameFormat::Format_RGBA8888)));
215 if (!m_texturePool.texturesDirty())
218 QVideoFrameTextures *textures = m_texturePool.updateTextures(*m_rhi, *rub);
222 QRhiShaderResourceBinding bindings[4];
224 *(b++) = QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
227 auto fmt = m_texturePool.currentFrame().surfaceFormat();
228 auto textureDesc = QVideoTextureHelper::textureDescription(fmt.pixelFormat());
230 for (
int i = 0; i < textureDesc->nplanes; ++i)
231 (*b++) = QRhiShaderResourceBinding::sampledTexture(
232 i + 1, QRhiShaderResourceBinding::FragmentStage, textures->texture(i),
233 m_textureSampler.get());
234 m_shaderResourceBindings->setBindings(bindings, b);
235 m_shaderResourceBindings->create();
239 if (!m_graphicsPipeline)
240 m_graphicsPipeline.reset(m_rhi->newGraphicsPipeline());
242 setupGraphicsPipeline(m_graphicsPipeline.get(), m_shaderResourceBindings.get(), format);
246void QVideoWindowPrivate::updateSubtitle(QRhiResourceUpdateBatch *rub,
const QSize &frameSize)
248 m_subtitleDirty =
false;
249 m_hasSubtitle = !m_texturePool.currentFrame().subtitleText().isEmpty();
253 m_subtitleLayout.update(frameSize, m_texturePool.currentFrame().subtitleText());
254 QSize size = m_subtitleLayout.bounds.size().toSize();
256 QImage img = m_subtitleLayout.toImage();
258 m_subtitleTexture.reset(m_rhi->newTexture(QRhiTexture::RGBA8, size));
259 m_subtitleTexture->create();
260 rub->uploadTexture(m_subtitleTexture.get(), img);
262 QRhiShaderResourceBinding bindings[2];
264 bindings[0] = QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
265 m_subtitleUniformBuf.get());
267 bindings[1] = QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage,
268 m_subtitleTexture.get(), m_textureSampler.get());
269 m_subtitleResourceBindings->setBindings(bindings, bindings + 2);
270 m_subtitleResourceBindings->create();
272 if (!m_subtitlePipeline) {
273 m_subtitlePipeline.reset(m_rhi->newGraphicsPipeline());
275 QRhiGraphicsPipeline::TargetBlend blend;
277 m_subtitlePipeline->setTargetBlends({ blend });
278 setupGraphicsPipeline(m_subtitlePipeline.get(), m_subtitleResourceBindings.get(), QVideoFrameFormat(QSize(1, 1), QVideoFrameFormat::Format_RGBA8888));
309void QVideoWindowPrivate::render()
314 if (!q->isExposed() || !isExposed)
317 QRect rect(0, 0, q->width(), q->height());
320 if (backingStore->size() != q->size())
321 backingStore->resize(q->size());
323 backingStore->beginPaint(rect);
325 QPaintDevice *device = backingStore->paintDevice();
328 QPainter painter(device);
330 QVideoFrame frame = m_texturePool.currentFrame();
331 frame.paint(&painter, rect, { Qt::black, aspectRatioMode });
334 backingStore->endPaint();
335 backingStore->flush(rect);
339 const VideoTransformation frameTransformation =
340 qNormalizedFrameTransformation(m_texturePool.currentFrame().surfaceFormat());
341 const QSize frameSize = qRotatedFramePresentationSize(m_texturePool.currentFrame());
342 const QSize scaled = frameSize.scaled(rect.size(), aspectRatioMode);
343 QRect videoRect = QRect(QPoint(0, 0), scaled);
344 videoRect.moveCenter(rect.center());
345 QRect subtitleRect = videoRect.intersected(rect);
347 if (!m_hasSwapChain || (m_swapChain->currentPixelSize() != m_swapChain->surfacePixelSize()))
350 const auto requiredSwapChainFormat =
351 qGetRequiredSwapChainFormat(m_texturePool.currentFrame().surfaceFormat());
352 if (qShouldUpdateSwapChainFormat(m_swapChain.get(), requiredSwapChainFormat)) {
354 m_swapChain->setFormat(requiredSwapChainFormat);
361 QRhi::FrameOpResult r = m_rhi->beginFrame(m_swapChain.get());
363 if (r == QRhi::FrameOpSwapChainOutOfDate) {
367 r = m_rhi->beginFrame(m_swapChain.get());
369 if (r != QRhi::FrameOpSuccess) {
370 qWarning(
"beginFrame failed with %d, retry", r);
375 QRhiResourceUpdateBatch *rub = m_rhi->nextResourceUpdateBatch();
377 if (!m_vertexBufReady) {
378 m_vertexBufReady =
true;
379 rub->uploadStaticBuffer(m_vertexBuf.get(), g_vw_quad);
384 if (m_subtitleDirty || m_subtitleLayout.videoSize != subtitleRect.size())
385 updateSubtitle(rub, subtitleRect.size());
387 const float mirrorFrame = frameTransformation.mirroredHorizontallyAfterRotation ? -1.f : 1.f;
388 const float xscale = mirrorFrame *
float(videoRect.width()) /
float(rect.width());
389 const float yscale = -1.f *
float(videoRect.height()) /
float(rect.height());
391 QMatrix4x4 transform;
392 transform.scale(xscale, yscale);
395 if (m_swapChain->format() == QRhiSwapChain::HDRExtendedSrgbLinear) {
396 auto info = m_swapChain->hdrInfo();
397 if (info.limitsType == QRhiSwapChainHdrInfo::ColorComponentValue)
398 maxNits = 100 * info.limits.colorComponentValue.maxColorComponentValue;
400 maxNits = info.limits.luminanceInNits.maxLuminance;
403 QByteArray uniformData;
404 QVideoTextureHelper::updateUniformData(&uniformData, m_rhi.get(),
405 m_texturePool.currentFrame().surfaceFormat(),
406 m_texturePool.currentFrame(), transform, 1.f, maxNits);
407 rub->updateDynamicBuffer(m_uniformBuf.get(), 0, uniformData.size(), uniformData.constData());
411 st.translate(0, -2.f * (
float(m_subtitleLayout.bounds.center().y()) +
float(subtitleRect.top()))/
float(rect.height()) + 1.f);
412 st.scale(
float(m_subtitleLayout.bounds.width())/
float(rect.width()),
413 -1.f *
float(m_subtitleLayout.bounds.height())/
float(rect.height()));
415 QByteArray uniformData;
416 QVideoFrameFormat fmt(m_subtitleLayout.bounds.size().toSize(), QVideoFrameFormat::Format_ARGB8888);
417 QVideoTextureHelper::updateUniformData(&uniformData, m_rhi.get(), fmt, QVideoFrame(), st,
419 rub->updateDynamicBuffer(m_subtitleUniformBuf.get(), 0, uniformData.size(), uniformData.constData());
422 QRhiCommandBuffer *cb = m_swapChain->currentFrameCommandBuffer();
423 cb->beginPass(m_swapChain->currentFrameRenderTarget(), Qt::black, { 1.0f, 0 }, rub);
424 cb->setGraphicsPipeline(m_graphicsPipeline.get());
425 auto size = m_swapChain->currentPixelSize();
426 cb->setViewport({ 0, 0,
float(size.width()),
float(size.height()) });
427 cb->setShaderResources(m_shaderResourceBindings.get());
429 const quint32 vertexOffset = quint32(
sizeof(
float)) * 16 * frameTransformation.rotationIndex();
430 const QRhiCommandBuffer::VertexInput vbufBinding(m_vertexBuf.get(), vertexOffset);
431 cb->setVertexInput(0, 1, &vbufBinding);
435 cb->setGraphicsPipeline(m_subtitlePipeline.get());
436 cb->setShaderResources(m_subtitleResourceBindings.get());
437 const QRhiCommandBuffer::VertexInput vbufBinding(m_vertexBuf.get(), 0);
438 cb->setVertexInput(0, 1, &vbufBinding);
444 m_rhi->endFrame(m_swapChain.get());
446 m_texturePool.onFrameEndInvoked();