Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Laszlo Agocs
qtrhi
Commits
be7bda45
Commit
be7bda45
authored
Mar 11, 2019
by
Laszlo Agocs
Browse files
Standalone qtrhi with qbakedshader
parent
db048497
Changes
234
Hide whitespace changes
Inline
Side-by-side
README.md
deleted
100644 → 0
View file @
db048497

Experiments for a Rendering Hardware Interface abstraction for future Qt (QtRhi)
========================================================================
The API and its backends (Vulkan, OpenGL (ES) 2.0, Direct3D 11, Metal) are
reasonably complete in the sense that it should be possible to bring up a Qt
Quick renderer on top of them (using Vulkan-style GLSL as the "common" shading
language - translation seems to work pretty well for now, including to HLSL and
MSL). Other than that this is highly experimental with a long todo list, and the
API will change in arbitrary ways. It nonetheless shows what a possible future
direction for the Qt graphics stack could be.
Experiments for more modern graphics shader management in future Qt (QtShaderTools)
===================================================================
Uses https://github.com/KhronosGroup/SPIRV-Cross and https://github.com/KhronosGroup/glslang
QShaderBaker: Compile (Vulkan-flavor) GLSL to SPIR-V. Generate reflection info.
Translate to HLSL, MSL, and various GLSL versions. Optionally rewrite vertex
shaders to make them suitable for Qt Quick scenegraph batching. Pack all this
into conveniently (de)serializable QBakedShader instances. Complemented by a
command-line tool (qsb) to allow doing the expensive work offline. This
optionally allows invoking fxc or metal/metallib to include compiled bytecode
for HLSL and MSL as well.
Documentation
=============
Generated docs are now online at https://alpqr.github.io
In action
=========
Needs Qt 5.12. Tested on Windows 10 with MSVC2015 and 2017, and macOS 10.14 with XCode 10.
Screenshots from the test application demonstrating basic drawing, pipeline
state (blending, depth), indexed drawing, texturing, and rendering into a
texture. All using the same code and the same two sets of vertex and fragment
shaders, with the only difference being in the QWindow setup.




Additionally, check
https://git.qt.io/laagocs/qtrhi/blob/master/examples/rhi/hellominimalcrossgfxtriangle/hellominimalcrossgfxtriangle.cpp
for a single-source, cross-API example of drawing a triangle.
examples/rhi/compressedtexture_bc1/compressedtexture_bc1.pro
View file @
be7bda45
TEMPLATE
=
app
QT
+=
shadertools
rhi
QT
+=
rhi
SOURCES
=
\
compressedtexture_bc1
.
cpp
...
...
examples/rhi/compressedtexture_bc1_subupload/compressedtexture_bc1_subupload.pro
View file @
be7bda45
TEMPLATE
=
app
QT
+=
shadertools
rhi
QT
+=
rhi
SOURCES
=
\
compressedtexture_bc1_subupload
.
cpp
...
...
examples/rhi/cubemap/cubemap.pro
View file @
be7bda45
TEMPLATE
=
app
QT
+=
shadertools
rhi
QT
+=
rhi
SOURCES
=
\
cubemap
.
cpp
...
...
examples/rhi/hellominimalcrossgfxtriangle/hellominimalcrossgfxtriangle.pro
View file @
be7bda45
TEMPLATE
=
app
QT
+=
shadertools
rhi
QT
+=
rhi
SOURCES
=
\
hellominimalcrossgfxtriangle
.
cpp
...
...
examples/rhi/imguidemo/imguidemo.pro
View file @
be7bda45
TEMPLATE
=
app
QT
+=
shadertools
rhi
QT
+=
rhi
SOURCES
=
\
imguidemo
.
cpp
\
...
...
examples/rhi/msaarenderbuffer/msaarenderbuffer.pro
View file @
be7bda45
TEMPLATE
=
app
QT
+=
shadertools
rhi
QT
+=
rhi
SOURCES
=
\
msaarenderbuffer
.
cpp
...
...
examples/rhi/msaatexture/msaatexture.pro
View file @
be7bda45
TEMPLATE
=
app
QT
+=
shadertools
rhi
QT
+=
rhi
SOURCES
=
\
msaatexture
.
cpp
...
...
examples/rhi/multiwindow/multiwindow.pro
View file @
be7bda45
TEMPLATE
=
app
QT
+=
shadertools
rhi
widgets
QT
+=
rhi
widgets
SOURCES
=
\
multiwindow
.
cpp
...
...
examples/rhi/multiwindow_threaded/multiwindow_threaded.cpp
View file @
be7bda45
...
...
@@ -338,9 +338,6 @@ Renderer::~Renderer()
#endif
}
bool
useRsh
=
false
;
QRhiResourceSharingHost
*
rsh
=
nullptr
;
void
Renderer
::
createRhi
()
{
if
(
r
)
...
...
@@ -349,17 +346,9 @@ void Renderer::createRhi()
qDebug
()
<<
"renderer"
<<
this
<<
"creating rhi"
;
QRhi
::
Flags
rhiFlags
=
QRhi
::
EnableProfiling
;
if
(
useRsh
)
{
qDebug
(
"Using QRhiResourceSharingHost"
);
if
(
!
rsh
)
rsh
=
new
QRhiResourceSharingHost
;
}
#ifndef QT_NO_OPENGL
if
(
graphicsApi
==
OpenGL
)
{
QRhiGles2InitParams
params
;
if
(
useRsh
)
params
.
resourceSharingHost
=
rsh
;
params
.
fallbackSurface
=
fallbackSurface
;
params
.
window
=
window
;
r
=
QRhi
::
create
(
QRhi
::
OpenGLES2
,
&
params
,
rhiFlags
);
...
...
@@ -369,8 +358,6 @@ void Renderer::createRhi()
#if QT_CONFIG(vulkan)
if
(
graphicsApi
==
Vulkan
)
{
QRhiVulkanInitParams
params
;
if
(
useRsh
)
params
.
resourceSharingHost
=
rsh
;
params
.
inst
=
instance
;
params
.
window
=
window
;
r
=
QRhi
::
create
(
QRhi
::
Vulkan
,
&
params
,
rhiFlags
);
...
...
@@ -380,8 +367,6 @@ void Renderer::createRhi()
#ifdef Q_OS_WIN
if
(
graphicsApi
==
D3D11
)
{
QRhiD3D11InitParams
params
;
if
(
useRsh
)
params
.
resourceSharingHost
=
rsh
;
params
.
enableDebugLayer
=
true
;
r
=
QRhi
::
create
(
QRhi
::
D3D11
,
&
params
,
rhiFlags
);
}
...
...
@@ -390,8 +375,6 @@ void Renderer::createRhi()
#ifdef Q_OS_DARWIN
if
(
graphicsApi
==
Metal
)
{
QRhiMetalInitParams
params
;
if
(
useRsh
)
params
.
resourceSharingHost
=
rsh
;
r
=
QRhi
::
create
(
QRhi
::
Metal
,
&
params
,
rhiFlags
);
}
#endif
...
...
@@ -808,20 +791,13 @@ int main(int argc, char **argv)
QLatin1String
(
"This application tests rendering on a separate thread per window, with dedicated QRhi instances and resources. "
"
\n\n
This is the same concept as the Qt Quick Scenegraph's threaded render loop. This should allow rendering to the different windows "
"without unintentionally throttling each other's threads."
"
\n\n
Can also be used to exercise creating sets of windows that can
\"
see
\"
each others' resources by using the same graphics device. "
"(although no application-side QRhiResource is reused here)"
"
\n\n
Using API: "
)
+
graphicsApiName
());
info
->
setReadOnly
(
true
);
layout
->
addWidget
(
info
);
QCheckBox
*
rshCb
=
new
QCheckBox
(
QLatin1String
(
"Use QRhiResourceSharingHost for new window
\n
(use same device, "
"e.g. VkDevice+VkQueue on Vulkan, ID3D11Device+Context on D3D; or sharing contexts on OpenGL)"
));
rshCb
->
setChecked
(
false
);
layout
->
addWidget
(
rshCb
);
QLabel
*
label
=
new
QLabel
(
QLatin1String
(
"Window and thread count: 0"
));
layout
->
addWidget
(
label
);
QPushButton
*
btn
=
new
QPushButton
(
QLatin1String
(
"New window"
));
QObject
::
connect
(
btn
,
&
QPushButton
::
clicked
,
btn
,
[
label
,
&
winCount
,
rshCb
]
{
useRsh
=
rshCb
->
isChecked
();
QObject
::
connect
(
btn
,
&
QPushButton
::
clicked
,
btn
,
[
label
,
&
winCount
]
{
winCount
+=
1
;
label
->
setText
(
QString
::
asprintf
(
"Window count: %d"
,
winCount
));
createWindow
();
...
...
@@ -845,8 +821,6 @@ int main(int argc, char **argv)
delete
wr
.
window
;
}
delete
rsh
;
#if QT_CONFIG(vulkan)
delete
instance
;
#endif
...
...
examples/rhi/multiwindow_threaded/multiwindow_threaded.pro
View file @
be7bda45
TEMPLATE
=
app
QT
+=
shadertools
rhi
widgets
QT
+=
rhi
widgets
SOURCES
=
\
multiwindow_threaded
.
cpp
\
...
...
examples/rhi/offscreen/offscreen.pro
View file @
be7bda45
TEMPLATE
=
app
CONFIG
+=
console
QT
+=
shadertools
rhi
QT
+=
rhi
SOURCES
=
\
offscreen
.
cpp
...
...
examples/rhi/rhi.pro
View file @
be7bda45
...
...
@@ -12,7 +12,6 @@ SUBDIRS += \
multiwindow_threaded
\
imguidemo
\
triquadcube
\
sharedresource
\
offscreen
qtConfig
(
vulkan
)
{
...
...
examples/rhi/sharedresource/sharedresource.cpp
deleted
100644 → 0
View file @
db048497
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
// Demonstrates using the same QRhiTexture with two QRhi instances.
#include <QGuiApplication>
#include <QCommandLineParser>
#include <QWindow>
#include <QPlatformSurfaceEvent>
#include <QElapsedTimer>
#include <QBakedShader>
#include <QFile>
#include <QOffscreenSurface>
#ifndef QT_NO_OPENGL
#include <QRhiGles2InitParams>
#endif
#if QT_CONFIG(vulkan)
#include <QLoggingCategory>
#include <QRhiVulkanInitParams>
#endif
#ifdef Q_OS_WIN
#include <QRhiD3D11InitParams>
#endif
#ifdef Q_OS_DARWIN
#include <QRhiMetalInitParams>
#endif
enum
GraphicsApi
{
OpenGL
,
Vulkan
,
D3D11
,
Metal
};
static
GraphicsApi
graphicsApi
;
static
QString
graphicsApiName
()
{
switch
(
graphicsApi
)
{
case
OpenGL
:
return
QLatin1String
(
"OpenGL 2.x"
);
case
Vulkan
:
return
QLatin1String
(
"Vulkan"
);
case
D3D11
:
return
QLatin1String
(
"Direct3D 11"
);
case
Metal
:
return
QLatin1String
(
"Metal"
);
default:
break
;
}
return
QString
();
}
#if QT_CONFIG(vulkan)
QVulkanInstance
*
vkinst
=
nullptr
;
int
activeRhiCount
=
0
;
QRhiResourceSharingHost
*
rsh
=
nullptr
;
QRhiTexture
*
tex
=
nullptr
;
#endif
void
createRhi
(
QWindow
*
window
,
QRhi
**
rhi
,
QOffscreenSurface
**
fallbackSurface
)
{
// This is what makes the difference here - create a single
// QRhiResourceSharingHost and associate all QRhis with it.
if
(
!
rsh
)
rsh
=
new
QRhiResourceSharingHost
;
#ifndef QT_NO_OPENGL
if
(
graphicsApi
==
OpenGL
)
{
*
fallbackSurface
=
QRhiGles2InitParams
::
newFallbackSurface
();
QRhiGles2InitParams
params
;
params
.
resourceSharingHost
=
rsh
;
params
.
fallbackSurface
=
*
fallbackSurface
;
params
.
window
=
window
;
*
rhi
=
QRhi
::
create
(
QRhi
::
OpenGLES2
,
&
params
);
}
#endif
#if QT_CONFIG(vulkan)
if
(
graphicsApi
==
Vulkan
)
{
QRhiVulkanInitParams
params
;
params
.
resourceSharingHost
=
rsh
;
params
.
inst
=
vkinst
;
params
.
window
=
window
;
*
rhi
=
QRhi
::
create
(
QRhi
::
Vulkan
,
&
params
);
}
#endif
#ifdef Q_OS_WIN
if
(
graphicsApi
==
D3D11
)
{
QRhiD3D11InitParams
params
;
params
.
resourceSharingHost
=
rsh
;
params
.
enableDebugLayer
=
true
;
*
rhi
=
QRhi
::
create
(
QRhi
::
D3D11
,
&
params
);
}
#endif
#ifdef Q_OS_DARWIN
if
(
graphicsApi
==
Metal
)
{
QRhiMetalInitParams
params
;
params
.
resourceSharingHost
=
rsh
;
*
rhi
=
QRhi
::
create
(
QRhi
::
Metal
,
&
params
);
}
#endif
if
(
!*
rhi
)
qFatal
(
"Failed to create RHI backend"
);
}
class
Window
:
public
QWindow
{
public:
Window
(
const
QString
&
title
,
const
QColor
&
bgColor
,
int
windowNumber
);
~
Window
();
protected:
void
init
();
void
releaseResources
();
void
resizeSwapChain
();
void
releaseSwapChain
();
void
render
();
void
exposeEvent
(
QExposeEvent
*
)
override
;
bool
event
(
QEvent
*
)
override
;
QRhi
*
m_rhi
=
nullptr
;
QOffscreenSurface
*
m_fallbackSurface
=
nullptr
;
QColor
m_bgColor
;
int
m_windowNumber
;
bool
m_running
=
false
;
bool
m_notExposed
=
false
;
bool
m_newlyExposed
=
false
;
QMatrix4x4
m_proj
;
float
m_rotation
=
0
;
QVector
<
QRhiResource
*>
m_releasePool
;
bool
m_hasSwapChain
=
false
;
QRhiSwapChain
*
m_sc
=
nullptr
;
QRhiRenderBuffer
*
m_ds
=
nullptr
;
QRhiRenderPassDescriptor
*
m_rp
=
nullptr
;
QRhiResourceUpdateBatch
*
initialUpdates
=
nullptr
;
QRhiBuffer
*
vbuf
=
nullptr
;
QRhiBuffer
*
ibuf
=
nullptr
;
QRhiBuffer
*
ubuf
=
nullptr
;
QRhiSampler
*
sampler
=
nullptr
;
QRhiShaderResourceBindings
*
srb
=
nullptr
;
QRhiGraphicsPipeline
*
ps
=
nullptr
;
};
Window
::
Window
(
const
QString
&
title
,
const
QColor
&
bgColor
,
int
windowNumber
)
:
m_bgColor
(
bgColor
),
m_windowNumber
(
windowNumber
)
{
switch
(
graphicsApi
)
{
case
OpenGL
:
setSurfaceType
(
OpenGLSurface
);
break
;
case
Vulkan
:
setSurfaceType
(
VulkanSurface
);
setVulkanInstance
(
vkinst
);
break
;
case
D3D11
:
setSurfaceType
(
OpenGLSurface
);
// not a typo
break
;
case
Metal
:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
setSurfaceType
(
MetalSurface
);
#endif
break
;
default:
break
;
}
resize
(
800
,
600
);
setTitle
(
title
);
}
Window
::~
Window
()
{
releaseResources
();
}
void
Window
::
exposeEvent
(
QExposeEvent
*
)
{
// initialize and start rendering when the window becomes usable for graphics purposes
if
(
isExposed
()
&&
!
m_running
)
{
m_running
=
true
;
init
();
resizeSwapChain
();
render
();
}
// stop pushing frames when not exposed (or size is 0)
if
((
!
isExposed
()
||
(
m_hasSwapChain
&&
m_sc
->
surfacePixelSize
().
isEmpty
()))
&&
m_running
)
m_notExposed
=
true
;
// continue when exposed again and the surface has a valid size.
// note that the surface size can be (0, 0) even though size() reports a valid one...
if
(
isExposed
()
&&
m_running
&&
m_notExposed
&&
!
m_sc
->
surfacePixelSize
().
isEmpty
())
{
m_notExposed
=
false
;
m_newlyExposed
=
true
;
render
();
}
}
bool
Window
::
event
(
QEvent
*
e
)
{
switch
(
e
->
type
())
{
case
QEvent
::
UpdateRequest
:
render
();
break
;
case
QEvent
::
PlatformSurface
:
// this is the proper time to tear down the swapchain (while the native window and surface are still around)
if
(
static_cast
<
QPlatformSurfaceEvent
*>
(
e
)
->
surfaceEventType
()
==
QPlatformSurfaceEvent
::
SurfaceAboutToBeDestroyed
)
releaseSwapChain
();
break
;
default:
break
;
}
return
QWindow
::
event
(
e
);
}
static
float
quadVert
[]
=
{
-
0.5
f
,
0.5
f
,
0.0
f
,
0.0
f
,
-
0.5
f
,
-
0.5
f
,
0.0
f
,
1.0
f
,
0.5
f
,
-
0.5
f
,
1.0
f
,
1.0
f
,
0.5
f
,
0.5
f
,
1.0
f
,
0.0
f
};
static
quint16
quadIndex
[]
=
{
0
,
1
,
2
,
0
,
2
,
3
};
QBakedShader
getShader
(
const
QString
&
name
)
{
QFile
f
(
name
);
if
(
f
.
open
(
QIODevice
::
ReadOnly
))
return
QBakedShader
::
fromSerialized
(
f
.
readAll
());
return
QBakedShader
();
}
void
Window
::
init
()
{
createRhi
(
this
,
&
m_rhi
,
&
m_fallbackSurface
);
++
activeRhiCount
;
m_sc
=
m_rhi
->
newSwapChain
();
m_ds
=
m_rhi
->
newRenderBuffer
(
QRhiRenderBuffer
::
DepthStencil
,
QSize
(),
// no need to set the size yet
1
,
QRhiRenderBuffer
::
UsedWithSwapChainOnly
);
m_releasePool
<<
m_ds
;
m_sc
->
setWindow
(
this
);
m_sc
->
setDepthStencil
(
m_ds
);
m_rp
=
m_sc
->
newCompatibleRenderPassDescriptor
();
m_releasePool
<<
m_rp
;
m_sc
->
setRenderPassDescriptor
(
m_rp
);
vbuf
=
m_rhi
->
newBuffer
(
QRhiBuffer
::
Immutable
,
QRhiBuffer
::
VertexBuffer
,
sizeof
(
quadVert
));
m_releasePool
<<
vbuf
;
vbuf
->
build
();
ibuf
=
m_rhi
->
newBuffer
(
QRhiBuffer
::
Immutable
,
QRhiBuffer
::
IndexBuffer
,
sizeof
(
quadIndex
));
m_releasePool
<<
ibuf
;
ibuf
->
build
();
ubuf
=
m_rhi
->
newBuffer
(
QRhiBuffer
::
Dynamic
,
QRhiBuffer
::
UniformBuffer
,
68
);
ubuf
->
build
();
m_releasePool
<<
ubuf
;
QImage
image
;
bool
newTex
=
false
;
if
(
!
tex
)
{
newTex
=
true
;
image
.
load
(
QLatin1String
(
":/qt256.png"
));
tex
=
m_rhi
->
newTexture
(
QRhiTexture
::
RGBA8
,
image
.
size
());
Q_ASSERT
(
tex
->
isShareable
());
tex
->
build
();
}
sampler
=
m_rhi
->
newSampler
(
QRhiSampler
::
Linear
,
QRhiSampler
::
Linear
,
QRhiSampler
::
None
,
QRhiSampler
::
ClampToEdge
,
QRhiSampler
::
ClampToEdge
);
m_releasePool
<<
sampler
;
sampler
->
build
();
srb
=
m_rhi
->
newShaderResourceBindings
();
m_releasePool
<<
srb
;
srb
->
setBindings
({
QRhiShaderResourceBinding
::
uniformBuffer
(
0
,
QRhiShaderResourceBinding
::
VertexStage
|
QRhiShaderResourceBinding
::
FragmentStage
,
ubuf
),
QRhiShaderResourceBinding
::
sampledTexture
(
1
,
QRhiShaderResourceBinding
::
FragmentStage
,
tex
,
sampler
)
});
srb
->
build
();
ps
=
m_rhi
->
newGraphicsPipeline
();
m_releasePool
<<
ps
;
ps
->
setShaderStages
({
{
QRhiGraphicsShaderStage
::
Vertex
,
getShader
(
QLatin1String
(
":/texture.vert.qsb"
))
},
{
QRhiGraphicsShaderStage
::
Fragment
,
getShader
(
QLatin1String
(
":/texture.frag.qsb"
))
}
});
QRhiVertexInputLayout
inputLayout
;
inputLayout
.
setBindings
({
{
4
*
sizeof
(
float
)
}
});
inputLayout
.
setAttributes
({
{
0
,
0
,
QRhiVertexInputAttribute
::
Float2
,
0
},
{
0
,
1
,
QRhiVertexInputAttribute
::
Float2
,
2
*
sizeof
(
float
)
}
});
ps
->
setVertexInputLayout
(
inputLayout
);
ps
->
setShaderResourceBindings
(
srb
);
ps
->
setRenderPassDescriptor
(
m_rp
);
ps
->
build
();
initialUpdates
=
m_rhi
->
nextResourceUpdateBatch
();
initialUpdates
->
uploadStaticBuffer
(
vbuf
,
0
,
sizeof
(
quadVert
),
quadVert
);
initialUpdates
->
uploadStaticBuffer
(
ibuf
,
quadIndex
);