mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2025-07-01 23:02:23 +08:00
qt 6.5.1 original
This commit is contained in:
49
tests/manual/examples/opengl/computegles31/CMakeLists.txt
Normal file
49
tests/manual/examples/opengl/computegles31/CMakeLists.txt
Normal file
@ -0,0 +1,49 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(computegles31 LANGUAGES CXX)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/opengl/computegles31")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(computegles31
|
||||
glwindow.cpp glwindow.h
|
||||
main.cpp
|
||||
)
|
||||
|
||||
set_target_properties(computegles31 PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
|
||||
target_link_libraries(computegles31 PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::OpenGL
|
||||
)
|
||||
|
||||
# Resources:
|
||||
set(computegles31_resource_files
|
||||
"Qt-logo-medium.png"
|
||||
)
|
||||
|
||||
qt_add_resources(computegles31 "computegles31"
|
||||
PREFIX
|
||||
"/"
|
||||
FILES
|
||||
${computegles31_resource_files}
|
||||
)
|
||||
|
||||
install(TARGETS computegles31
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
BIN
tests/manual/examples/opengl/computegles31/Qt-logo-medium.png
Normal file
BIN
tests/manual/examples/opengl/computegles31/Qt-logo-medium.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
11
tests/manual/examples/opengl/computegles31/computegles31.pro
Normal file
11
tests/manual/examples/opengl/computegles31/computegles31.pro
Normal file
@ -0,0 +1,11 @@
|
||||
QT += opengl
|
||||
|
||||
HEADERS = $$PWD/glwindow.h
|
||||
|
||||
SOURCES = $$PWD/glwindow.cpp \
|
||||
$$PWD/main.cpp
|
||||
|
||||
RESOURCES += computegles31.qrc
|
||||
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/opengl/computegles31
|
||||
INSTALLS += target
|
@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>Qt-logo-medium.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
372
tests/manual/examples/opengl/computegles31/glwindow.cpp
Normal file
372
tests/manual/examples/opengl/computegles31/glwindow.cpp
Normal file
@ -0,0 +1,372 @@
|
||||
// Copyright (C) 2017 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "glwindow.h"
|
||||
#include <QImage>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLExtraFunctions>
|
||||
#include <QOpenGLVertexArrayObject>
|
||||
#include <QtGui/qopengl.h>
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
#include <math.h>
|
||||
|
||||
#ifndef GL_READ_WRITE
|
||||
#define GL_READ_WRITE 0x88BA
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGBA8
|
||||
#define GL_RGBA8 0x8058
|
||||
#endif
|
||||
|
||||
#ifndef GL_SHADER_IMAGE_ACCESS_BARRIER_BIT
|
||||
#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020
|
||||
#endif
|
||||
|
||||
GLWindow::GLWindow()
|
||||
{
|
||||
const float animationStart = 0.0;
|
||||
const float animationEnd = 10.0;
|
||||
const float animationLength = 1000;
|
||||
|
||||
m_animationGroup = new QSequentialAnimationGroup(this);
|
||||
m_animationGroup->setLoopCount(-1);
|
||||
|
||||
m_animationForward = new QPropertyAnimation(this, QByteArrayLiteral("blurRadius"));
|
||||
m_animationForward->setStartValue(animationStart);
|
||||
m_animationForward->setEndValue(animationEnd);
|
||||
m_animationForward->setDuration(animationLength);
|
||||
m_animationGroup->addAnimation(m_animationForward);
|
||||
|
||||
m_animationBackward = new QPropertyAnimation(this, QByteArrayLiteral("blurRadius"));
|
||||
m_animationBackward->setStartValue(animationEnd);
|
||||
m_animationBackward->setEndValue(animationStart);
|
||||
m_animationBackward->setDuration(animationLength);
|
||||
m_animationGroup->addAnimation(m_animationBackward);
|
||||
|
||||
m_animationGroup->start();
|
||||
}
|
||||
|
||||
GLWindow::~GLWindow()
|
||||
{
|
||||
makeCurrent();
|
||||
delete m_texImageInput;
|
||||
delete m_texImageProcessed;
|
||||
delete m_texImageTmp;
|
||||
delete m_shaderDisplay;
|
||||
delete m_shaderComputeH;
|
||||
delete m_shaderComputeV;
|
||||
delete m_animationGroup;
|
||||
delete m_animationForward;
|
||||
delete m_animationBackward;
|
||||
delete m_vao;
|
||||
}
|
||||
|
||||
void GLWindow::setBlurRadius(float blurRadius)
|
||||
{
|
||||
int radius = int(blurRadius);
|
||||
if (radius != m_blurRadius) {
|
||||
m_blurRadius = radius;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void GLWindow::setAnimating(bool animate)
|
||||
{
|
||||
m_animate = animate;
|
||||
if (animate)
|
||||
m_animationGroup->start();
|
||||
else
|
||||
m_animationGroup->stop();
|
||||
}
|
||||
|
||||
void GLWindow::keyPressEvent(QKeyEvent *e)
|
||||
{
|
||||
if (e->key() == Qt::Key_Space) { // pause
|
||||
setAnimating(!m_animate);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static const char *vsDisplaySource =
|
||||
"const vec4 vertices[4] = vec4[4] (\n"
|
||||
" vec4( -1.0, 1.0, 0.0, 1.0),\n"
|
||||
" vec4( -1.0, -1.0, 0.0, 1.0),\n"
|
||||
" vec4( 1.0, 1.0, 0.0, 1.0),\n"
|
||||
" vec4( 1.0, -1.0, 0.0, 1.0)\n"
|
||||
");\n"
|
||||
"const vec2 texCoords[4] = vec2[4] (\n"
|
||||
" vec2( 0.0, 1.0),\n"
|
||||
" vec2( 0.0, 0.0),\n"
|
||||
" vec2( 1.0, 1.0),\n"
|
||||
" vec2( 1.0, 0.0)\n"
|
||||
");\n"
|
||||
"out vec2 texCoord;\n"
|
||||
"uniform mat4 matProjection;\n"
|
||||
"uniform vec2 imageRatio;\n"
|
||||
"void main() {\n"
|
||||
" gl_Position = matProjection * ( vertices[gl_VertexID] * vec4(imageRatio,0,1) );\n"
|
||||
" texCoord = texCoords[gl_VertexID];\n"
|
||||
"}\n";
|
||||
|
||||
static const char *fsDisplaySource =
|
||||
"in lowp vec2 texCoord; \n"
|
||||
"uniform sampler2D samImage; \n"
|
||||
"layout(location = 0) out lowp vec4 color;\n"
|
||||
"void main() {\n"
|
||||
" lowp vec4 texColor = texture(samImage,texCoord);\n"
|
||||
" color = vec4(texColor.rgb, 1.0);\n"
|
||||
"}\n";
|
||||
|
||||
static const char *csComputeSourceV =
|
||||
"#define COMPUTEPATCHSIZE 10 // Setting this to 10 to comply with MAX_COMPUTE_WORK_GROUP_INVOCATIONS for both OpenGL and OpenGLES - see QTBUG-79374 \n"
|
||||
"#define IMGFMT rgba8 \n"
|
||||
"layout (local_size_x = COMPUTEPATCHSIZE, local_size_y = COMPUTEPATCHSIZE) in;\n"
|
||||
"layout(binding=0, IMGFMT) uniform readonly highp image2D inputImage; // Use a sampler to improve performance \n"
|
||||
"layout(binding=1, IMGFMT) uniform writeonly highp image2D resultImage;\n"
|
||||
"uniform int radius;\n"
|
||||
"const float cutoff = 2.2;\n"
|
||||
|
||||
"float expFactor() { // a function, otherwise MESA produces error: initializer of global variable `expFactor' must be a constant expression\n"
|
||||
" float sigma = clamp(float(radius) / cutoff,0.02,100.0);\n"
|
||||
" return 1.0 / (2.0 * sigma * sigma);\n"
|
||||
"}\n"
|
||||
|
||||
"float gaussian(float distance, float expfactor) {\n"
|
||||
" return exp( -(distance * distance) * expfactor);\n"
|
||||
"}\n"
|
||||
|
||||
"void main() {\n"
|
||||
" ivec2 imgSize = imageSize(resultImage);\n"
|
||||
" int x = int(gl_GlobalInvocationID.x);\n"
|
||||
" int y = int(gl_GlobalInvocationID.y);\n"
|
||||
" if ( (x >= imgSize.x) || (y >= imgSize.y) ) return;\n"
|
||||
" vec4 sumPixels = vec4(0.0);\n"
|
||||
" float sumWeights = 0.0;\n"
|
||||
" int left = clamp(x - radius, 0, imgSize.x - 1);\n"
|
||||
" int right = clamp(x + radius, 0, imgSize.x - 1);\n"
|
||||
" int top = clamp(y - radius, 0, imgSize.y - 1);\n"
|
||||
" int bottom = clamp(y + radius, 0, imgSize.y - 1);\n"
|
||||
" float expfactor = expFactor();\n"
|
||||
" for (int iY = top; iY <= bottom; iY++) {\n"
|
||||
" float dy = float(abs(iY - y));\n"
|
||||
" vec4 imgValue = imageLoad(inputImage, ivec2(x,iY));\n"
|
||||
" float weight = gaussian(dy, expfactor);\n"
|
||||
" sumWeights += weight;\n"
|
||||
" sumPixels += (imgValue * weight);\n"
|
||||
" }\n"
|
||||
" sumPixels /= sumWeights;\n"
|
||||
" imageStore(resultImage, ivec2(x,y), sumPixels);\n"
|
||||
"}\n";
|
||||
|
||||
static const char *csComputeSourceH =
|
||||
"#define COMPUTEPATCHSIZE 10 \n"
|
||||
"#define IMGFMT rgba8 \n"
|
||||
"layout (local_size_x = COMPUTEPATCHSIZE, local_size_y = COMPUTEPATCHSIZE) in;\n"
|
||||
"layout(binding=0, IMGFMT) uniform readonly highp image2D inputImage; // Use a sampler to improve performance \n"
|
||||
"layout(binding=1, IMGFMT) uniform writeonly highp image2D resultImage;\n"
|
||||
"uniform int radius;\n"
|
||||
"const float cutoff = 2.2;\n"
|
||||
|
||||
"float expFactor() { // a function, otherwise MESA produces error: initializer of global variable `expFactor' must be a constant expression\n"
|
||||
" float sigma = clamp(float(radius) / cutoff,0.02,100.0);\n"
|
||||
" return 1.0 / (2.0 * sigma * sigma);\n"
|
||||
"}\n"
|
||||
|
||||
"float gaussian(float distance, float expfactor) {\n"
|
||||
" return exp( -(distance * distance) * expfactor);\n"
|
||||
"}\n"
|
||||
|
||||
"void main() {\n"
|
||||
" ivec2 imgSize = imageSize(resultImage);\n"
|
||||
" int x = int(gl_GlobalInvocationID.x);\n"
|
||||
" int y = int(gl_GlobalInvocationID.y);\n"
|
||||
" if ( (x >= imgSize.x) || (y >= imgSize.y) ) return;\n"
|
||||
" vec4 sumPixels = vec4(0.0);\n"
|
||||
" float sumWeights = 0.0;\n"
|
||||
" int left = clamp(x - radius, 0, imgSize.x - 1);\n"
|
||||
" int right = clamp(x + radius, 0, imgSize.x - 1);\n"
|
||||
" int top = clamp(y - radius, 0, imgSize.y - 1);\n"
|
||||
" int bottom = clamp(y + radius, 0, imgSize.y - 1);\n"
|
||||
" float expfactor = expFactor();\n"
|
||||
" for (int iX = left; iX <= right; iX++) {\n"
|
||||
" float dx = float(abs(iX - x));\n"
|
||||
" vec4 imgValue = imageLoad(inputImage, ivec2(iX,y));\n"
|
||||
" float weight = gaussian(dx, expfactor);\n"
|
||||
" sumWeights += weight;\n"
|
||||
" sumPixels += (imgValue * weight);\n"
|
||||
" }\n"
|
||||
" sumPixels /= sumWeights;\n"
|
||||
" imageStore(resultImage, ivec2(x,y), sumPixels);\n"
|
||||
"}\n";
|
||||
|
||||
|
||||
|
||||
QByteArray versionedShaderCode(const char *src)
|
||||
{
|
||||
QByteArray versionedSrc;
|
||||
|
||||
if (QOpenGLContext::currentContext()->isOpenGLES())
|
||||
versionedSrc.append(QByteArrayLiteral("#version 310 es\n"));
|
||||
else
|
||||
versionedSrc.append(QByteArrayLiteral("#version 430 core\n"));
|
||||
|
||||
versionedSrc.append(src);
|
||||
return versionedSrc;
|
||||
}
|
||||
|
||||
void computeProjection(int winWidth, int winHeight, int imgWidth, int imgHeight, QMatrix4x4 &outProjection, QSizeF &outQuadSize)
|
||||
{
|
||||
float ratioImg = float(imgWidth) / float(imgHeight);
|
||||
float ratioCanvas = float(winWidth) / float(winHeight);
|
||||
|
||||
float correction = ratioImg / ratioCanvas;
|
||||
float rescaleFactor = 1.0f;
|
||||
float quadWidth = 1.0f;
|
||||
float quadHeight = 1.0f;
|
||||
|
||||
if (correction < 1.0f) // canvas larger than image -- height = 1.0, vertical black bands
|
||||
{
|
||||
quadHeight = 1.0f;
|
||||
quadWidth = 1.0f * ratioImg;
|
||||
rescaleFactor = ratioCanvas;
|
||||
correction = 1.0f / rescaleFactor;
|
||||
}
|
||||
else // image larger than canvas -- width = 1.0, horizontal black bands
|
||||
{
|
||||
quadWidth = 1.0f;
|
||||
quadHeight = 1.0f / ratioImg;
|
||||
correction = 1.0f / ratioCanvas;
|
||||
}
|
||||
|
||||
const float frustumWidth = 1.0f * rescaleFactor;
|
||||
const float frustumHeight = 1.0f * rescaleFactor * correction;
|
||||
|
||||
outProjection = QMatrix4x4();
|
||||
outProjection.ortho(
|
||||
-frustumWidth,
|
||||
frustumWidth,
|
||||
-frustumHeight,
|
||||
frustumHeight,
|
||||
-1.0f,
|
||||
1.0f);
|
||||
outQuadSize = QSizeF(quadWidth,quadHeight);
|
||||
}
|
||||
|
||||
void GLWindow::initializeGL()
|
||||
{
|
||||
QOpenGLContext *ctx = QOpenGLContext::currentContext();
|
||||
qDebug() << "Got a "
|
||||
<< ctx->format().majorVersion()
|
||||
<< "."
|
||||
<< ctx->format().minorVersion()
|
||||
<< ((ctx->format().renderableType() == QSurfaceFormat::OpenGLES) ? (" GLES") : (" GL"))
|
||||
<< " context";
|
||||
|
||||
QImage img(":/Qt-logo-medium.png");
|
||||
Q_ASSERT(!img.isNull());
|
||||
delete m_texImageInput;
|
||||
m_texImageInput = new QOpenGLTexture(img.convertToFormat(QImage::Format_RGBA8888).mirrored());
|
||||
|
||||
delete m_texImageTmp;
|
||||
m_texImageTmp = new QOpenGLTexture(QOpenGLTexture::Target2D);
|
||||
m_texImageTmp->setFormat(m_texImageInput->format());
|
||||
m_texImageTmp->setSize(m_texImageInput->width(),m_texImageInput->height());
|
||||
m_texImageTmp->allocateStorage(QOpenGLTexture::RGBA,QOpenGLTexture::UInt8); // WTF?
|
||||
|
||||
delete m_texImageProcessed;
|
||||
m_texImageProcessed = new QOpenGLTexture(QOpenGLTexture::Target2D);
|
||||
m_texImageProcessed->setFormat(m_texImageInput->format());
|
||||
m_texImageProcessed->setSize(m_texImageInput->width(),m_texImageInput->height());
|
||||
m_texImageProcessed->allocateStorage(QOpenGLTexture::RGBA,QOpenGLTexture::UInt8);
|
||||
|
||||
m_texImageProcessed->setMagnificationFilter(QOpenGLTexture::Linear);
|
||||
m_texImageProcessed->setMinificationFilter(QOpenGLTexture::Linear);
|
||||
m_texImageProcessed->setWrapMode(QOpenGLTexture::ClampToEdge);
|
||||
|
||||
delete m_shaderDisplay;
|
||||
m_shaderDisplay = new QOpenGLShaderProgram;
|
||||
// Prepend the correct version directive to the sources. The rest is the
|
||||
// same, thanks to the common GLSL syntax.
|
||||
m_shaderDisplay->addShaderFromSourceCode(QOpenGLShader::Vertex, versionedShaderCode(vsDisplaySource));
|
||||
m_shaderDisplay->addShaderFromSourceCode(QOpenGLShader::Fragment, versionedShaderCode(fsDisplaySource));
|
||||
m_shaderDisplay->link();
|
||||
|
||||
delete m_shaderComputeV;
|
||||
m_shaderComputeV = new QOpenGLShaderProgram;
|
||||
m_shaderComputeV->addShaderFromSourceCode(QOpenGLShader::Compute, versionedShaderCode(csComputeSourceV));
|
||||
m_shaderComputeV->link();
|
||||
|
||||
delete m_shaderComputeH;
|
||||
m_shaderComputeH = new QOpenGLShaderProgram;
|
||||
m_shaderComputeH->addShaderFromSourceCode(QOpenGLShader::Compute, versionedShaderCode(csComputeSourceH));
|
||||
m_shaderComputeH->link();
|
||||
|
||||
// Create a VAO. Not strictly required for ES 3, but it is for plain OpenGL core context.
|
||||
m_vao = new QOpenGLVertexArrayObject;
|
||||
m_vao->create();
|
||||
}
|
||||
|
||||
void GLWindow::resizeGL(int w, int h)
|
||||
{
|
||||
computeProjection(w,h,m_texImageInput->width(),m_texImageInput->height(),m_proj,m_quadSize);
|
||||
}
|
||||
|
||||
QSize getWorkGroups(int workGroupSize, const QSize &imageSize)
|
||||
{
|
||||
int x = imageSize.width();
|
||||
x = (x % workGroupSize) ? (x / workGroupSize) + 1 : (x / workGroupSize);
|
||||
int y = imageSize.height();
|
||||
y = (y % workGroupSize) ? (y / workGroupSize) + 1 : (y / workGroupSize);
|
||||
return QSize(x,y);
|
||||
}
|
||||
|
||||
void GLWindow::paintGL()
|
||||
{
|
||||
// Now use QOpenGLExtraFunctions instead of QOpenGLFunctions as we want to
|
||||
// do more than what GL(ES) 2.0 offers.
|
||||
QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions();
|
||||
|
||||
|
||||
// Process input image
|
||||
QSize workGroups = getWorkGroups(10, QSize(m_texImageInput->width(), m_texImageInput->height()));
|
||||
// Pass 1
|
||||
f->glBindImageTexture(0, m_texImageInput->textureId(), 0, 0, 0, GL_READ_WRITE, GL_RGBA8);
|
||||
f->glBindImageTexture(1, m_texImageTmp->textureId(), 0, 0, 0, GL_READ_WRITE, GL_RGBA8);
|
||||
m_shaderComputeV->bind();
|
||||
m_shaderComputeV->setUniformValue("radius",m_blurRadius);
|
||||
f->glDispatchCompute(workGroups.width(),workGroups.height(),1);
|
||||
f->glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
|
||||
m_shaderComputeV->release();
|
||||
// Pass 2
|
||||
f->glBindImageTexture(0, m_texImageTmp->textureId(), 0, 0, 0, GL_READ_WRITE, GL_RGBA8);
|
||||
f->glBindImageTexture(1, m_texImageProcessed->textureId(), 0, 0, 0, GL_READ_WRITE, GL_RGBA8);
|
||||
m_shaderComputeH->bind();
|
||||
m_shaderComputeH->setUniformValue("radius",m_blurRadius);
|
||||
f->glDispatchCompute(workGroups.width(),workGroups.height(),1);
|
||||
f->glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
|
||||
m_shaderComputeH->release();
|
||||
// Compute cleanup
|
||||
f->glBindImageTexture(0, 0, 0, 0, 0, GL_READ_WRITE, GL_RGBA8);
|
||||
f->glBindImageTexture(1, 0, 0, 0, 0, GL_READ_WRITE, GL_RGBA8);
|
||||
|
||||
// Display processed image
|
||||
f->glClearColor(0, 0, 0, 1);
|
||||
f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
m_texImageProcessed->bind(0);
|
||||
m_shaderDisplay->bind();
|
||||
m_shaderDisplay->setUniformValue("matProjection",m_proj);
|
||||
m_shaderDisplay->setUniformValue("imageRatio",m_quadSize);
|
||||
m_shaderDisplay->setUniformValue("samImage",0);
|
||||
m_vao->bind();
|
||||
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
m_vao->release();
|
||||
m_shaderDisplay->release();
|
||||
m_texImageProcessed->release(0);
|
||||
}
|
||||
|
63
tests/manual/examples/opengl/computegles31/glwindow.h
Normal file
63
tests/manual/examples/opengl/computegles31/glwindow.h
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright (C) 2017 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef GLWIDGET_H
|
||||
#define GLWIDGET_H
|
||||
|
||||
#include <QOpenGLWindow>
|
||||
#include <QOpenGLTexture>
|
||||
#include <QMatrix4x4>
|
||||
#include <QVector3D>
|
||||
#include <QKeyEvent>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QSequentialAnimationGroup>
|
||||
#include <QRectF>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QOpenGLTexture;
|
||||
class QOpenGLShaderProgram;
|
||||
class QOpenGLBuffer;
|
||||
class QOpenGLVertexArrayObject;
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class GLWindow : public QOpenGLWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float blurRadius READ blurRadius WRITE setBlurRadius)
|
||||
|
||||
public:
|
||||
GLWindow();
|
||||
~GLWindow();
|
||||
|
||||
void initializeGL() override;
|
||||
void resizeGL(int w, int h) override;
|
||||
void paintGL() override;
|
||||
|
||||
float blurRadius() const { return m_blurRadius; }
|
||||
void setBlurRadius(float blurRadius);
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void setAnimating(bool animate);
|
||||
|
||||
private:
|
||||
QPropertyAnimation *m_animationForward = nullptr;
|
||||
QPropertyAnimation *m_animationBackward = nullptr;
|
||||
QSequentialAnimationGroup *m_animationGroup;
|
||||
QOpenGLTexture *m_texImageInput = nullptr;
|
||||
QOpenGLTexture *m_texImageTmp = nullptr;
|
||||
QOpenGLTexture *m_texImageProcessed = nullptr;
|
||||
QOpenGLShaderProgram *m_shaderDisplay = nullptr;
|
||||
QOpenGLShaderProgram *m_shaderComputeV = nullptr;
|
||||
QOpenGLShaderProgram *m_shaderComputeH = nullptr;
|
||||
QMatrix4x4 m_proj;
|
||||
QSizeF m_quadSize;
|
||||
|
||||
int m_blurRadius = 0;
|
||||
bool m_animate = true;
|
||||
QOpenGLVertexArrayObject *m_vao = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
101
tests/manual/examples/opengl/computegles31/main.cpp
Normal file
101
tests/manual/examples/opengl/computegles31/main.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
// Copyright (C) 2017 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QSurfaceFormat>
|
||||
#include <QOffscreenSurface>
|
||||
#include <QOpenGLContext>
|
||||
#include <QDebug>
|
||||
#include <QPair>
|
||||
#include "glwindow.h"
|
||||
|
||||
bool OGLSupports(int major, int minor, bool gles = false, QSurfaceFormat::OpenGLContextProfile profile = QSurfaceFormat::NoProfile)
|
||||
{
|
||||
QOpenGLContext ctx;
|
||||
QSurfaceFormat fmt;
|
||||
fmt.setVersion(major, minor);
|
||||
if (gles) {
|
||||
fmt.setRenderableType(QSurfaceFormat::OpenGLES);
|
||||
} else {
|
||||
fmt.setRenderableType(QSurfaceFormat::OpenGL);
|
||||
fmt.setProfile(profile);
|
||||
}
|
||||
|
||||
ctx.setFormat(fmt);
|
||||
ctx.create();
|
||||
if (!ctx.isValid())
|
||||
return false;
|
||||
int ctxMajor = ctx.format().majorVersion();
|
||||
int ctxMinor = ctx.format().minorVersion();
|
||||
bool isGles = (ctx.format().renderableType() == QSurfaceFormat::OpenGLES);
|
||||
|
||||
if (isGles != gles) return false;
|
||||
if (ctxMajor < major) return false;
|
||||
if (ctxMajor == major && ctxMinor < minor)
|
||||
return false;
|
||||
if (!gles && ctx.format().profile() != profile)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
qDebug() << "Support for GL 2.0 noprof "<<( OGLSupports(2,0,false) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 2.0 core "<<( OGLSupports(2,0,false, QSurfaceFormat::CoreProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 2.0 compat "<<( OGLSupports(2,0,false, QSurfaceFormat::CompatibilityProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 2.1 noprof "<<( OGLSupports(2,1,false) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 2.1 core "<<( OGLSupports(2,1,false, QSurfaceFormat::CoreProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 2.1 compat "<<( OGLSupports(2,1,false, QSurfaceFormat::CompatibilityProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 3.0 noprof "<<( OGLSupports(3,0,false) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 3.0 core "<<( OGLSupports(3,0,false, QSurfaceFormat::CoreProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 3.0 compat "<<( OGLSupports(3,0,false, QSurfaceFormat::CompatibilityProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 3.1 noprof "<<( OGLSupports(3,1,false) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 3.1 core "<<( OGLSupports(3,1,false, QSurfaceFormat::CoreProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 3.1 compat "<<( OGLSupports(3,1,false, QSurfaceFormat::CompatibilityProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 3.2 core "<<( OGLSupports(3,2,false,QSurfaceFormat::CoreProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 3.2 compat "<<( OGLSupports(3,2,false,QSurfaceFormat::CompatibilityProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 3.3 core "<<( OGLSupports(3,3,false,QSurfaceFormat::CoreProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 3.3 compat "<<( OGLSupports(3,3,false,QSurfaceFormat::CompatibilityProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 4.0 core "<<( OGLSupports(4,0,false,QSurfaceFormat::CoreProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 4.0 compat "<<( OGLSupports(4,0,false,QSurfaceFormat::CompatibilityProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 4.1 core "<<( OGLSupports(4,1,false,QSurfaceFormat::CoreProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 4.1 compat "<<( OGLSupports(4,1,false,QSurfaceFormat::CompatibilityProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 4.2 core "<<( OGLSupports(4,2,false,QSurfaceFormat::CoreProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 4.2 compat "<<( OGLSupports(4,2,false,QSurfaceFormat::CompatibilityProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 4.3 core "<<( OGLSupports(4,3,false,QSurfaceFormat::CoreProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 4.3 compat "<<( OGLSupports(4,3,false,QSurfaceFormat::CompatibilityProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 4.4 core "<<( OGLSupports(4,4,false,QSurfaceFormat::CoreProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 4.4 compat "<<( OGLSupports(4,4,false,QSurfaceFormat::CompatibilityProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 4.5 core "<<( OGLSupports(4,5,false,QSurfaceFormat::CoreProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GL 4.5 compat "<<( OGLSupports(4,5,false,QSurfaceFormat::CompatibilityProfile) ? "yes" : "no");
|
||||
qDebug() << "Support for GLES 2.0 "<<( OGLSupports(2,0,true) ? "yes" : "no");
|
||||
qDebug() << "Support for GLES 3.0 "<<( OGLSupports(3,0,true) ? "yes" : "no");
|
||||
qDebug() << "Support for GLES 3.1 "<<( OGLSupports(3,1,true) ? "yes" : "no");
|
||||
qDebug() << "Support for GLES 3.2 "<<( OGLSupports(3,2,true) ? "yes" : "no");
|
||||
|
||||
QSurfaceFormat fmt;
|
||||
fmt.setDepthBufferSize(24);
|
||||
|
||||
// Request OpenGL ES 3.1 context, as this is a GLES example. If not available, go for OpenGL 4.3 core.
|
||||
if (OGLSupports(3,1,true)) {
|
||||
qDebug("Requesting 3.1 GLES context");
|
||||
fmt.setVersion(3, 1);
|
||||
fmt.setRenderableType(QSurfaceFormat::OpenGLES);
|
||||
} else if (OGLSupports(4,3,false,QSurfaceFormat::CoreProfile)) {
|
||||
qDebug("Requesting 4.3 core context");
|
||||
fmt.setVersion(4, 3);
|
||||
fmt.setRenderableType(QSurfaceFormat::OpenGL);
|
||||
fmt.setProfile(QSurfaceFormat::CoreProfile);
|
||||
} else {
|
||||
qWarning("Error: This system does not support OpenGL Compute Shaders! Exiting.");
|
||||
return -1;
|
||||
}
|
||||
QSurfaceFormat::setDefaultFormat(fmt);
|
||||
|
||||
GLWindow glWindow;
|
||||
glWindow.showMaximized();
|
||||
|
||||
return app.exec();
|
||||
}
|
39
tests/manual/examples/opengl/hellowindow/CMakeLists.txt
Normal file
39
tests/manual/examples/opengl/hellowindow/CMakeLists.txt
Normal file
@ -0,0 +1,39 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(hellowindow LANGUAGES CXX)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/opengl/hellowindow")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(hellowindow
|
||||
hellowindow.cpp hellowindow.h
|
||||
main.cpp
|
||||
)
|
||||
|
||||
set_target_properties(hellowindow PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
|
||||
target_link_libraries(hellowindow PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::CorePrivate
|
||||
Qt6::Gui
|
||||
Qt6::GuiPrivate
|
||||
Qt6::OpenGL
|
||||
)
|
||||
|
||||
install(TARGETS hellowindow
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
305
tests/manual/examples/opengl/hellowindow/hellowindow.cpp
Normal file
305
tests/manual/examples/opengl/hellowindow/hellowindow.cpp
Normal file
@ -0,0 +1,305 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "hellowindow.h"
|
||||
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QRandomGenerator>
|
||||
#include <qmath.h>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
Renderer::Renderer(const QSurfaceFormat &format, Renderer *share, QScreen *screen)
|
||||
: m_initialized(false)
|
||||
, m_format(format)
|
||||
{
|
||||
m_context = new QOpenGLContext(this);
|
||||
if (screen)
|
||||
m_context->setScreen(screen);
|
||||
m_context->setFormat(format);
|
||||
if (share)
|
||||
m_context->setShareContext(share->m_context);
|
||||
m_context->create();
|
||||
|
||||
m_backgroundColor = QColor::fromRgbF(0.1f, 0.1f, 0.2f, 1.0f);
|
||||
m_backgroundColor.setRed(QRandomGenerator::global()->bounded(64));
|
||||
m_backgroundColor.setGreen(QRandomGenerator::global()->bounded(128));
|
||||
m_backgroundColor.setBlue(QRandomGenerator::global()->bounded(256));
|
||||
}
|
||||
|
||||
HelloWindow::HelloWindow(const QSharedPointer<Renderer> &renderer, QScreen *screen)
|
||||
: m_colorIndex(0), m_renderer(renderer)
|
||||
{
|
||||
setSurfaceType(QWindow::OpenGLSurface);
|
||||
setFlags(Qt::Window | Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint);
|
||||
|
||||
setGeometry(QRect(10, 10, 640, 480));
|
||||
|
||||
setFormat(renderer->format());
|
||||
if (screen)
|
||||
setScreen(screen);
|
||||
|
||||
create();
|
||||
|
||||
updateColor();
|
||||
|
||||
connect(renderer.data(), &Renderer::requestUpdate, this, &QWindow::requestUpdate);
|
||||
}
|
||||
|
||||
void HelloWindow::exposeEvent(QExposeEvent *)
|
||||
{
|
||||
if (isExposed())
|
||||
render();
|
||||
}
|
||||
|
||||
bool HelloWindow::event(QEvent *ev)
|
||||
{
|
||||
if (ev->type() == QEvent::UpdateRequest && isExposed())
|
||||
render();
|
||||
return QWindow::event(ev);
|
||||
}
|
||||
|
||||
void HelloWindow::render()
|
||||
{
|
||||
static QElapsedTimer timer;
|
||||
if (!timer.isValid())
|
||||
timer.start();
|
||||
qreal a = (qreal)(((timer.elapsed() * 3) % 36000) / 100.0);
|
||||
auto call = [this, r = m_renderer.data(), a, c = color()]() { r->render(this, a, c); };
|
||||
QMetaObject::invokeMethod(m_renderer.data(), call);
|
||||
}
|
||||
|
||||
void HelloWindow::mousePressEvent(QMouseEvent *)
|
||||
{
|
||||
updateColor();
|
||||
}
|
||||
|
||||
QColor HelloWindow::color() const
|
||||
{
|
||||
return m_color;
|
||||
}
|
||||
|
||||
void HelloWindow::updateColor()
|
||||
{
|
||||
QColor colors[] =
|
||||
{
|
||||
QColor(100, 255, 0),
|
||||
QColor(0, 100, 255)
|
||||
};
|
||||
|
||||
m_color = colors[m_colorIndex];
|
||||
m_colorIndex = 1 - m_colorIndex;
|
||||
}
|
||||
|
||||
void Renderer::render(HelloWindow *surface, qreal angle, const QColor &color)
|
||||
{
|
||||
if (!m_context->makeCurrent(surface))
|
||||
return;
|
||||
|
||||
QSize viewSize = surface->size();
|
||||
|
||||
if (!m_initialized) {
|
||||
initialize();
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
QOpenGLFunctions *f = m_context->functions();
|
||||
f->glViewport(0, 0, viewSize.width() * surface->devicePixelRatio(), viewSize.height() * surface->devicePixelRatio());
|
||||
f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
f->glClearColor(m_backgroundColor.redF(), m_backgroundColor.greenF(), m_backgroundColor.blueF(), m_backgroundColor.alphaF());
|
||||
f->glFrontFace(GL_CW);
|
||||
f->glCullFace(GL_FRONT);
|
||||
f->glEnable(GL_CULL_FACE);
|
||||
f->glEnable(GL_DEPTH_TEST);
|
||||
|
||||
m_program->bind();
|
||||
m_vbo.bind();
|
||||
|
||||
m_program->enableAttributeArray(vertexAttr);
|
||||
m_program->enableAttributeArray(normalAttr);
|
||||
m_program->setAttributeBuffer(vertexAttr, GL_FLOAT, 0, 3);
|
||||
const int verticesSize = vertices.count() * 3 * sizeof(GLfloat);
|
||||
m_program->setAttributeBuffer(normalAttr, GL_FLOAT, verticesSize, 3);
|
||||
|
||||
QMatrix4x4 modelview;
|
||||
modelview.rotate(angle, 0.0f, 1.0f, 0.0f);
|
||||
modelview.rotate(angle, 1.0f, 0.0f, 0.0f);
|
||||
modelview.rotate(angle, 0.0f, 0.0f, 1.0f);
|
||||
modelview.translate(0.0f, -0.2f, 0.0f);
|
||||
|
||||
m_program->setUniformValue(matrixUniform, modelview);
|
||||
m_program->setUniformValue(colorUniform, color);
|
||||
|
||||
m_context->functions()->glDrawArrays(GL_TRIANGLES, 0, vertices.size());
|
||||
|
||||
m_context->swapBuffers(surface);
|
||||
|
||||
emit requestUpdate();
|
||||
}
|
||||
|
||||
Q_GLOBAL_STATIC(QMutex, initMutex)
|
||||
|
||||
void Renderer::initialize()
|
||||
{
|
||||
// Threaded shader compilation can confuse some drivers. Avoid it.
|
||||
QMutexLocker lock(initMutex());
|
||||
|
||||
QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
|
||||
vshader->compileSourceCode(
|
||||
"attribute highp vec4 vertex;"
|
||||
"attribute mediump vec3 normal;"
|
||||
"uniform mediump mat4 matrix;"
|
||||
"uniform lowp vec4 sourceColor;"
|
||||
"varying mediump vec4 color;"
|
||||
"void main(void)"
|
||||
"{"
|
||||
" vec3 toLight = normalize(vec3(0.0, 0.3, 1.0));"
|
||||
" float angle = max(dot(normal, toLight), 0.0);"
|
||||
" vec3 col = sourceColor.rgb;"
|
||||
" color = vec4(col * 0.2 + col * 0.8 * angle, 1.0);"
|
||||
" color = clamp(color, 0.0, 1.0);"
|
||||
" gl_Position = matrix * vertex;"
|
||||
"}");
|
||||
|
||||
QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
|
||||
fshader->compileSourceCode(
|
||||
"varying mediump vec4 color;"
|
||||
"void main(void)"
|
||||
"{"
|
||||
" gl_FragColor = color;"
|
||||
"}");
|
||||
|
||||
m_program = new QOpenGLShaderProgram(this);
|
||||
m_program->addShader(vshader);
|
||||
m_program->addShader(fshader);
|
||||
m_program->link();
|
||||
m_program->bind();
|
||||
|
||||
vertexAttr = m_program->attributeLocation("vertex");
|
||||
normalAttr = m_program->attributeLocation("normal");
|
||||
matrixUniform = m_program->uniformLocation("matrix");
|
||||
colorUniform = m_program->uniformLocation("sourceColor");
|
||||
|
||||
createGeometry();
|
||||
|
||||
m_vbo.create();
|
||||
m_vbo.bind();
|
||||
const int verticesSize = vertices.count() * 3 * sizeof(GLfloat);
|
||||
m_vbo.allocate(verticesSize * 2);
|
||||
m_vbo.write(0, vertices.constData(), verticesSize);
|
||||
m_vbo.write(verticesSize, normals.constData(), verticesSize);
|
||||
}
|
||||
|
||||
void Renderer::createGeometry()
|
||||
{
|
||||
vertices.clear();
|
||||
normals.clear();
|
||||
|
||||
qreal x1 = +0.06f;
|
||||
qreal y1 = -0.14f;
|
||||
qreal x2 = +0.14f;
|
||||
qreal y2 = -0.06f;
|
||||
qreal x3 = +0.08f;
|
||||
qreal y3 = +0.00f;
|
||||
qreal x4 = +0.30f;
|
||||
qreal y4 = +0.22f;
|
||||
|
||||
quad(x1, y1, x2, y2, y2, x2, y1, x1);
|
||||
quad(x3, y3, x4, y4, y4, x4, y3, x3);
|
||||
|
||||
extrude(x1, y1, x2, y2);
|
||||
extrude(x2, y2, y2, x2);
|
||||
extrude(y2, x2, y1, x1);
|
||||
extrude(y1, x1, x1, y1);
|
||||
extrude(x3, y3, x4, y4);
|
||||
extrude(x4, y4, y4, x4);
|
||||
extrude(y4, x4, y3, x3);
|
||||
|
||||
const int NumSectors = 100;
|
||||
const qreal sectorAngle = 2 * qreal(M_PI) / NumSectors;
|
||||
for (int i = 0; i < NumSectors; ++i) {
|
||||
qreal angle = i * sectorAngle;
|
||||
qreal x5 = 0.30 * qSin(angle);
|
||||
qreal y5 = 0.30 * qCos(angle);
|
||||
qreal x6 = 0.20 * qSin(angle);
|
||||
qreal y6 = 0.20 * qCos(angle);
|
||||
|
||||
angle += sectorAngle;
|
||||
qreal x7 = 0.20 * qSin(angle);
|
||||
qreal y7 = 0.20 * qCos(angle);
|
||||
qreal x8 = 0.30 * qSin(angle);
|
||||
qreal y8 = 0.30 * qCos(angle);
|
||||
|
||||
quad(x5, y5, x6, y6, x7, y7, x8, y8);
|
||||
|
||||
extrude(x6, y6, x7, y7);
|
||||
extrude(x8, y8, x5, y5);
|
||||
}
|
||||
|
||||
for (int i = 0;i < vertices.size();i++)
|
||||
vertices[i] *= 2.0f;
|
||||
}
|
||||
|
||||
void Renderer::quad(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4)
|
||||
{
|
||||
vertices << QVector3D(x1, y1, -0.05f);
|
||||
vertices << QVector3D(x2, y2, -0.05f);
|
||||
vertices << QVector3D(x4, y4, -0.05f);
|
||||
|
||||
vertices << QVector3D(x3, y3, -0.05f);
|
||||
vertices << QVector3D(x4, y4, -0.05f);
|
||||
vertices << QVector3D(x2, y2, -0.05f);
|
||||
|
||||
QVector3D n = QVector3D::normal
|
||||
(QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(x4 - x1, y4 - y1, 0.0f));
|
||||
|
||||
normals << n;
|
||||
normals << n;
|
||||
normals << n;
|
||||
|
||||
normals << n;
|
||||
normals << n;
|
||||
normals << n;
|
||||
|
||||
vertices << QVector3D(x4, y4, 0.05f);
|
||||
vertices << QVector3D(x2, y2, 0.05f);
|
||||
vertices << QVector3D(x1, y1, 0.05f);
|
||||
|
||||
vertices << QVector3D(x2, y2, 0.05f);
|
||||
vertices << QVector3D(x4, y4, 0.05f);
|
||||
vertices << QVector3D(x3, y3, 0.05f);
|
||||
|
||||
n = QVector3D::normal
|
||||
(QVector3D(x2 - x4, y2 - y4, 0.0f), QVector3D(x1 - x4, y1 - y4, 0.0f));
|
||||
|
||||
normals << n;
|
||||
normals << n;
|
||||
normals << n;
|
||||
|
||||
normals << n;
|
||||
normals << n;
|
||||
normals << n;
|
||||
}
|
||||
|
||||
void Renderer::extrude(qreal x1, qreal y1, qreal x2, qreal y2)
|
||||
{
|
||||
vertices << QVector3D(x1, y1, +0.05f);
|
||||
vertices << QVector3D(x2, y2, +0.05f);
|
||||
vertices << QVector3D(x1, y1, -0.05f);
|
||||
|
||||
vertices << QVector3D(x2, y2, -0.05f);
|
||||
vertices << QVector3D(x1, y1, -0.05f);
|
||||
vertices << QVector3D(x2, y2, +0.05f);
|
||||
|
||||
QVector3D n = QVector3D::normal
|
||||
(QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(0.0f, 0.0f, -0.1f));
|
||||
|
||||
normals << n;
|
||||
normals << n;
|
||||
normals << n;
|
||||
|
||||
normals << n;
|
||||
normals << n;
|
||||
normals << n;
|
||||
}
|
77
tests/manual/examples/opengl/hellowindow/hellowindow.h
Normal file
77
tests/manual/examples/opengl/hellowindow/hellowindow.h
Normal file
@ -0,0 +1,77 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef HELLOWINDOW_H
|
||||
#define HELLOWINDOW_H
|
||||
|
||||
#include <QWindow>
|
||||
|
||||
#include <QColor>
|
||||
#include <QMutex>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QSharedPointer>
|
||||
#include <QTimer>
|
||||
|
||||
class HelloWindow;
|
||||
|
||||
class Renderer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Renderer(const QSurfaceFormat &format, Renderer *share = nullptr,
|
||||
QScreen *screen = nullptr);
|
||||
|
||||
QSurfaceFormat format() const { return m_format; }
|
||||
|
||||
public slots:
|
||||
void render(HelloWindow *surface, qreal angle, const QColor &color);
|
||||
|
||||
signals:
|
||||
void requestUpdate();
|
||||
|
||||
private:
|
||||
void initialize();
|
||||
|
||||
void createGeometry();
|
||||
void createBubbles(int number);
|
||||
void quad(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4);
|
||||
void extrude(qreal x1, qreal y1, qreal x2, qreal y2);
|
||||
|
||||
QList<QVector3D> vertices;
|
||||
QList<QVector3D> normals;
|
||||
int vertexAttr;
|
||||
int normalAttr;
|
||||
int matrixUniform;
|
||||
int colorUniform;
|
||||
|
||||
bool m_initialized;
|
||||
QSurfaceFormat m_format;
|
||||
QOpenGLContext *m_context;
|
||||
QOpenGLShaderProgram *m_program;
|
||||
QOpenGLBuffer m_vbo;
|
||||
QColor m_backgroundColor;
|
||||
};
|
||||
|
||||
class HelloWindow : public QWindow
|
||||
{
|
||||
public:
|
||||
explicit HelloWindow(const QSharedPointer<Renderer> &renderer, QScreen *screen = nullptr);
|
||||
|
||||
QColor color() const;
|
||||
void updateColor();
|
||||
|
||||
protected:
|
||||
bool event(QEvent *ev) override;
|
||||
void exposeEvent(QExposeEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *) override;
|
||||
void render();
|
||||
|
||||
private:
|
||||
int m_colorIndex;
|
||||
QColor m_color;
|
||||
const QSharedPointer<Renderer> m_renderer;
|
||||
};
|
||||
|
||||
#endif // HELLOWINDOW_H
|
9
tests/manual/examples/opengl/hellowindow/hellowindow.pro
Normal file
9
tests/manual/examples/opengl/hellowindow/hellowindow.pro
Normal file
@ -0,0 +1,9 @@
|
||||
QT += gui-private core-private opengl
|
||||
|
||||
HEADERS += hellowindow.h
|
||||
SOURCES += hellowindow.cpp main.cpp
|
||||
|
||||
# install
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/opengl/hellowindow
|
||||
INSTALLS += target
|
||||
|
125
tests/manual/examples/opengl/hellowindow/main.cpp
Normal file
125
tests/manual/examples/opengl/hellowindow/main.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "hellowindow.h"
|
||||
|
||||
#include <qpa/qplatformintegration.h>
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QCommandLineOption>
|
||||
#include <QGuiApplication>
|
||||
#include <QScreen>
|
||||
#include <QThread>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
QCoreApplication::setApplicationName("Qt HelloWindow GL Example");
|
||||
QCoreApplication::setOrganizationName("QtProject");
|
||||
QCoreApplication::setApplicationVersion(QT_VERSION_STR);
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(QCoreApplication::applicationName());
|
||||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
QCommandLineOption multipleOption("multiple", "Create multiple windows");
|
||||
parser.addOption(multipleOption);
|
||||
QCommandLineOption multipleSampleOption("multisample", "Multisampling");
|
||||
parser.addOption(multipleSampleOption);
|
||||
QCommandLineOption multipleScreenOption("multiscreen", "Run on multiple screens");
|
||||
parser.addOption(multipleScreenOption);
|
||||
QCommandLineOption timeoutOption("timeout", "Close after 10s");
|
||||
parser.addOption(timeoutOption);
|
||||
parser.process(app);
|
||||
|
||||
// Some platforms can only have one window per screen. Therefore we need to differentiate.
|
||||
const bool multipleWindows = parser.isSet(multipleOption);
|
||||
const bool multipleScreens = parser.isSet(multipleScreenOption);
|
||||
|
||||
QScreen *screen = QGuiApplication::primaryScreen();
|
||||
|
||||
QRect screenGeometry = screen->availableGeometry();
|
||||
|
||||
QSurfaceFormat format;
|
||||
format.setDepthBufferSize(16);
|
||||
if (parser.isSet(multipleSampleOption))
|
||||
format.setSamples(4);
|
||||
|
||||
QPoint center = QPoint(screenGeometry.center().x(), screenGeometry.top() + 80);
|
||||
QSize windowSize(400, 320);
|
||||
int delta = 40;
|
||||
|
||||
QList<QWindow *> windows;
|
||||
QSharedPointer<Renderer> rendererA(new Renderer(format));
|
||||
|
||||
HelloWindow *windowA = new HelloWindow(rendererA);
|
||||
windowA->setGeometry(QRect(center, windowSize).translated(-windowSize.width() - delta / 2, 0));
|
||||
windowA->setTitle(QStringLiteral("Thread A - Context A"));
|
||||
windowA->setVisible(true);
|
||||
windows.prepend(windowA);
|
||||
|
||||
QList<QThread *> renderThreads;
|
||||
if (multipleWindows) {
|
||||
QSharedPointer<Renderer> rendererB(new Renderer(format, rendererA.data()));
|
||||
|
||||
QThread *renderThread = new QThread;
|
||||
rendererB->moveToThread(renderThread);
|
||||
renderThreads << renderThread;
|
||||
|
||||
HelloWindow *windowB = new HelloWindow(rendererA);
|
||||
windowB->setGeometry(QRect(center, windowSize).translated(delta / 2, 0));
|
||||
windowB->setTitle(QStringLiteral("Thread A - Context A"));
|
||||
windowB->setVisible(true);
|
||||
windows.prepend(windowB);
|
||||
|
||||
HelloWindow *windowC = new HelloWindow(rendererB);
|
||||
windowC->setGeometry(QRect(center, windowSize).translated(-windowSize.width() / 2, windowSize.height() + delta));
|
||||
windowC->setTitle(QStringLiteral("Thread B - Context B"));
|
||||
windowC->setVisible(true);
|
||||
windows.prepend(windowC);
|
||||
}
|
||||
if (multipleScreens) {
|
||||
for (int i = 1; i < QGuiApplication::screens().size(); ++i) {
|
||||
QScreen *screen = QGuiApplication::screens().at(i);
|
||||
QSharedPointer<Renderer> renderer(new Renderer(format, rendererA.data(), screen));
|
||||
|
||||
QThread *renderThread = new QThread;
|
||||
renderer->moveToThread(renderThread);
|
||||
renderThreads.prepend(renderThread);
|
||||
|
||||
QRect screenGeometry = screen->availableGeometry();
|
||||
QPoint center = screenGeometry.center();
|
||||
|
||||
QSize windowSize = screenGeometry.size() * 0.8;
|
||||
|
||||
HelloWindow *window = new HelloWindow(renderer, screen);
|
||||
window->setGeometry(QRect(center, windowSize).translated(-windowSize.width() / 2, -windowSize.height() / 2));
|
||||
|
||||
QChar id = QChar('B' + i);
|
||||
window->setTitle(QStringLiteral("Thread ") + id + QStringLiteral(" - Context ") + id);
|
||||
window->setVisible(true);
|
||||
windows.prepend(window);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < renderThreads.size(); ++i) {
|
||||
QObject::connect(qGuiApp, &QGuiApplication::lastWindowClosed, renderThreads.at(i), &QThread::quit);
|
||||
renderThreads.at(i)->start();
|
||||
}
|
||||
|
||||
// Quit after 10 seconds. For platforms that do not have windows that are closeable.
|
||||
if (parser.isSet(timeoutOption))
|
||||
QTimer::singleShot(10000, qGuiApp, &QCoreApplication::quit);
|
||||
|
||||
const int exitValue = app.exec();
|
||||
|
||||
for (int i = 0; i < renderThreads.size(); ++i) {
|
||||
renderThreads.at(i)->quit(); // some platforms may not have windows to close so ensure quit()
|
||||
renderThreads.at(i)->wait();
|
||||
}
|
||||
|
||||
qDeleteAll(windows);
|
||||
qDeleteAll(renderThreads);
|
||||
|
||||
return exitValue;
|
||||
}
|
37
tests/manual/examples/opengl/paintedwindow/CMakeLists.txt
Normal file
37
tests/manual/examples/opengl/paintedwindow/CMakeLists.txt
Normal file
@ -0,0 +1,37 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(paintedwindow LANGUAGES CXX)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/opengl/paintedwindow")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(paintedwindow
|
||||
main.cpp
|
||||
paintedwindow.cpp paintedwindow.h
|
||||
)
|
||||
|
||||
set_target_properties(paintedwindow PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
|
||||
target_link_libraries(paintedwindow PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::OpenGL
|
||||
)
|
||||
|
||||
install(TARGETS paintedwindow
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
18
tests/manual/examples/opengl/paintedwindow/main.cpp
Normal file
18
tests/manual/examples/opengl/paintedwindow/main.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QRect>
|
||||
|
||||
#include "paintedwindow.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
PaintedWindow window;
|
||||
window.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
185
tests/manual/examples/opengl/paintedwindow/paintedwindow.cpp
Normal file
185
tests/manual/examples/opengl/paintedwindow/paintedwindow.cpp
Normal file
@ -0,0 +1,185 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "paintedwindow.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLPaintDevice>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QScreen>
|
||||
#include <QTimer>
|
||||
|
||||
#include <qmath.h>
|
||||
|
||||
PaintedWindow::PaintedWindow()
|
||||
{
|
||||
QSurfaceFormat format;
|
||||
format.setStencilBufferSize(8);
|
||||
format.setSamples(4);
|
||||
|
||||
setSurfaceType(QWindow::OpenGLSurface);
|
||||
setFlags(Qt::Window | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint);
|
||||
setFormat(format);
|
||||
|
||||
create();
|
||||
|
||||
m_context = new QOpenGLContext(this);
|
||||
m_context->setFormat(format);
|
||||
m_context->create();
|
||||
|
||||
m_animation = new QPropertyAnimation(this, "rotation");
|
||||
m_animation->setStartValue(qreal(0));
|
||||
m_animation->setEndValue(qreal(1));
|
||||
m_animation->setDuration(500);
|
||||
|
||||
QRect screenGeometry = screen()->availableGeometry();
|
||||
|
||||
QPoint center = screenGeometry.center();
|
||||
QRect windowRect = screen()->isLandscape(screen()->orientation()) ? QRect(0, 0, 640, 480) : QRect(0, 0, 480, 640);
|
||||
setGeometry(QRect(center - windowRect.center(), windowRect.size()));
|
||||
|
||||
m_rotation = 0;
|
||||
|
||||
reportContentOrientationChange(screen()->orientation());
|
||||
|
||||
m_targetOrientation = contentOrientation();
|
||||
m_nextTargetOrientation = Qt::PrimaryOrientation;
|
||||
|
||||
connect(screen(), &QScreen::orientationChanged, this, &PaintedWindow::orientationChanged);
|
||||
connect(m_animation, &QAbstractAnimation::finished, this, &PaintedWindow::rotationDone);
|
||||
connect(this, &PaintedWindow::rotationChanged, this, QOverload<>::of(&PaintedWindow::paint));
|
||||
}
|
||||
|
||||
void PaintedWindow::exposeEvent(QExposeEvent *)
|
||||
{
|
||||
if (isExposed())
|
||||
paint();
|
||||
}
|
||||
|
||||
void PaintedWindow::mousePressEvent(QMouseEvent *)
|
||||
{
|
||||
Qt::ScreenOrientation o = contentOrientation();
|
||||
switch (o) {
|
||||
case Qt::LandscapeOrientation:
|
||||
orientationChanged(Qt::PortraitOrientation);
|
||||
break;
|
||||
case Qt::PortraitOrientation:
|
||||
orientationChanged(Qt::InvertedLandscapeOrientation);
|
||||
break;
|
||||
case Qt::InvertedLandscapeOrientation:
|
||||
orientationChanged(Qt::InvertedPortraitOrientation);
|
||||
break;
|
||||
case Qt::InvertedPortraitOrientation:
|
||||
orientationChanged(Qt::LandscapeOrientation);
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
paint();
|
||||
}
|
||||
|
||||
void PaintedWindow::orientationChanged(Qt::ScreenOrientation newOrientation)
|
||||
{
|
||||
if (contentOrientation() == newOrientation)
|
||||
return;
|
||||
|
||||
if (m_animation->state() == QAbstractAnimation::Running) {
|
||||
m_nextTargetOrientation = newOrientation;
|
||||
return;
|
||||
}
|
||||
|
||||
QRect rect(0, 0, width(), height());
|
||||
|
||||
m_prevImage = QImage(width(), height(), QImage::Format_ARGB32_Premultiplied);
|
||||
m_nextImage = QImage(width(), height(), QImage::Format_ARGB32_Premultiplied);
|
||||
m_prevImage.fill(0);
|
||||
m_nextImage.fill(0);
|
||||
|
||||
QPainter p;
|
||||
p.begin(&m_prevImage);
|
||||
p.setTransform(screen()->transformBetween(contentOrientation(), screen()->orientation(), rect));
|
||||
paint(&p, screen()->mapBetween(contentOrientation(), screen()->orientation(), rect));
|
||||
p.end();
|
||||
|
||||
p.begin(&m_nextImage);
|
||||
p.setTransform(screen()->transformBetween(newOrientation, screen()->orientation(), rect));
|
||||
paint(&p, screen()->mapBetween(newOrientation, screen()->orientation(), rect));
|
||||
p.end();
|
||||
|
||||
m_deltaRotation = screen()->angleBetween(newOrientation, contentOrientation());
|
||||
if (m_deltaRotation > 180)
|
||||
m_deltaRotation = 180 - m_deltaRotation;
|
||||
|
||||
m_targetOrientation = newOrientation;
|
||||
m_animation->start();
|
||||
}
|
||||
|
||||
void PaintedWindow::rotationDone()
|
||||
{
|
||||
reportContentOrientationChange(m_targetOrientation);
|
||||
if (m_nextTargetOrientation != Qt::PrimaryOrientation) {
|
||||
Q_ASSERT(m_animation->state() != QAbstractAnimation::Running);
|
||||
orientationChanged(m_nextTargetOrientation);
|
||||
m_nextTargetOrientation = Qt::PrimaryOrientation;
|
||||
}
|
||||
}
|
||||
|
||||
void PaintedWindow::setRotation(qreal r)
|
||||
{
|
||||
if (r != m_rotation) {
|
||||
m_rotation = r;
|
||||
emit rotationChanged(r);
|
||||
}
|
||||
}
|
||||
|
||||
void PaintedWindow::paint()
|
||||
{
|
||||
m_context->makeCurrent(this);
|
||||
|
||||
QRect rect(0, 0, width() * devicePixelRatio(), height() * devicePixelRatio());
|
||||
|
||||
QOpenGLPaintDevice device(size() * devicePixelRatio());
|
||||
QPainter painter(&device);
|
||||
|
||||
QPainterPath path;
|
||||
path.addEllipse(rect);
|
||||
painter.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
painter.fillRect(rect, Qt::transparent);
|
||||
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
painter.fillPath(path, Qt::blue);
|
||||
|
||||
if (contentOrientation() != m_targetOrientation) {
|
||||
painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
painter.save();
|
||||
painter.translate(width() / 2, height() / 2);
|
||||
painter.rotate(m_deltaRotation * m_rotation);
|
||||
painter.translate(-width() / 2, -height() / 2);
|
||||
painter.drawImage(0, 0, m_prevImage);
|
||||
painter.restore();
|
||||
painter.translate(width() / 2, height() / 2);
|
||||
painter.rotate(m_deltaRotation * m_rotation - m_deltaRotation);
|
||||
painter.translate(-width() / 2, -height() / 2);
|
||||
painter.setOpacity(m_rotation);
|
||||
painter.drawImage(0, 0, m_nextImage);
|
||||
} else {
|
||||
QRect mapped = screen()->mapBetween(contentOrientation(), screen()->orientation(), rect);
|
||||
|
||||
painter.setTransform(screen()->transformBetween(contentOrientation(), screen()->orientation(), rect));
|
||||
paint(&painter, mapped);
|
||||
painter.end();
|
||||
}
|
||||
|
||||
m_context->swapBuffers(this);
|
||||
}
|
||||
|
||||
void PaintedWindow::paint(QPainter *painter, const QRect &rect)
|
||||
{
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
QFont font;
|
||||
font.setPixelSize(64);
|
||||
painter->setFont(font);
|
||||
painter->drawText(rect, Qt::AlignCenter, "Hello");
|
||||
}
|
62
tests/manual/examples/opengl/paintedwindow/paintedwindow.h
Normal file
62
tests/manual/examples/opengl/paintedwindow/paintedwindow.h
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef PAINTEDWINDOW_H
|
||||
#define PAINTEDWINDOW_H
|
||||
|
||||
#include <QWindow>
|
||||
|
||||
#include <QtGui/qopengl.h>
|
||||
#include <QtOpenGL/qopenglshaderprogram.h>
|
||||
#include <QtOpenGL/qopenglframebufferobject.h>
|
||||
|
||||
#include <QPropertyAnimation>
|
||||
|
||||
#include <QColor>
|
||||
#include <QImage>
|
||||
#include <QTime>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QOpenGLContext;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class PaintedWindow : public QWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged)
|
||||
|
||||
public:
|
||||
PaintedWindow();
|
||||
|
||||
qreal rotation() const { return m_rotation; }
|
||||
|
||||
signals:
|
||||
void rotationChanged(qreal rotation);
|
||||
|
||||
private slots:
|
||||
void paint();
|
||||
void setRotation(qreal r);
|
||||
void orientationChanged(Qt::ScreenOrientation newOrientation);
|
||||
void rotationDone();
|
||||
|
||||
private:
|
||||
void exposeEvent(QExposeEvent *) override;
|
||||
void mousePressEvent(QMouseEvent *) override;
|
||||
|
||||
void paint(QPainter *painter, const QRect &rect);
|
||||
|
||||
QOpenGLContext *m_context;
|
||||
qreal m_rotation;
|
||||
|
||||
QImage m_prevImage;
|
||||
QImage m_nextImage;
|
||||
qreal m_deltaRotation;
|
||||
|
||||
Qt::ScreenOrientation m_targetOrientation;
|
||||
Qt::ScreenOrientation m_nextTargetOrientation;
|
||||
|
||||
QPropertyAnimation *m_animation;
|
||||
QTimer *m_paintTimer;
|
||||
};
|
||||
|
||||
#endif // PAINTEDWINDOW_H
|
@ -0,0 +1,8 @@
|
||||
QT += opengl
|
||||
|
||||
HEADERS += paintedwindow.h
|
||||
SOURCES += paintedwindow.cpp main.cpp
|
||||
|
||||
# install
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/opengl/paintedwindow
|
||||
INSTALLS += target
|
49
tests/manual/examples/opengl/qopenglwindow/CMakeLists.txt
Normal file
49
tests/manual/examples/opengl/qopenglwindow/CMakeLists.txt
Normal file
@ -0,0 +1,49 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(qopenglwindow LANGUAGES CXX)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/opengl/qopenglwindow")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(qopenglwindow
|
||||
background_renderer.cpp background_renderer.h
|
||||
main.cpp
|
||||
)
|
||||
|
||||
set_target_properties(qopenglwindow PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
|
||||
target_link_libraries(qopenglwindow PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::OpenGL
|
||||
)
|
||||
|
||||
# Resources:
|
||||
set(shaders_resource_files
|
||||
"background.frag"
|
||||
)
|
||||
|
||||
qt_add_resources(qopenglwindow "shaders"
|
||||
PREFIX
|
||||
"/"
|
||||
FILES
|
||||
${shaders_resource_files}
|
||||
)
|
||||
|
||||
install(TARGETS qopenglwindow
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
30
tests/manual/examples/opengl/qopenglwindow/background.frag
Normal file
30
tests/manual/examples/opengl/qopenglwindow/background.frag
Normal file
@ -0,0 +1,30 @@
|
||||
#define M_PI 3.14159265358979323846
|
||||
#define SPEED 10000.0
|
||||
|
||||
uniform int currentTime;
|
||||
uniform highp vec2 windowSize;
|
||||
|
||||
highp float noise(highp vec2 co)
|
||||
{
|
||||
return 0.5 * fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453);
|
||||
}
|
||||
|
||||
highp float curvSpeed()
|
||||
{
|
||||
return (mod(float(currentTime), SPEED) / SPEED) * (2.0 * M_PI);
|
||||
}
|
||||
|
||||
highp float curv(int curvCount)
|
||||
{
|
||||
highp float curv_y = 0.1 *(cos((gl_FragCoord.x / windowSize.x) * (float(curvCount * 2) * M_PI) - curvSpeed())) + 0.5;
|
||||
highp float frag_y = gl_FragCoord.y / windowSize.y;
|
||||
return 1.0 - abs(curv_y - frag_y);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
highp float coordNoise = noise(gl_FragCoord.xy);
|
||||
highp float proximity = smoothstep(0.85, 1.0, (curv(6) + 1.0) * (coordNoise ));
|
||||
highp vec3 color = vec3(coordNoise) * proximity;
|
||||
gl_FragColor = vec4(color, 1.0);
|
||||
}
|
@ -0,0 +1,167 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "background_renderer.h"
|
||||
|
||||
#include <qmath.h>
|
||||
#include <QFileInfo>
|
||||
#include <QTime>
|
||||
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLFunctions>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
static const char vertex_shader[] =
|
||||
"attribute highp vec3 vertexCoord;"
|
||||
"void main() {"
|
||||
" gl_Position = vec4(vertexCoord,1.0);"
|
||||
"}";
|
||||
|
||||
static const char fragment_shader[] =
|
||||
"void main() {"
|
||||
" gl_FragColor = vec4(0.0,1.0,0.0,1.0);"
|
||||
"}";
|
||||
|
||||
static const float vertices[] = { -1, -1, 0,
|
||||
-1, 1, 0,
|
||||
1, -1, 0,
|
||||
1, 1, 0 };
|
||||
|
||||
FragmentToy::FragmentToy(const QString &fragmentSource, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_recompile_shaders(true)
|
||||
{
|
||||
if (QFile::exists(fragmentSource)) {
|
||||
QFileInfo info(fragmentSource);
|
||||
m_fragment_file_last_modified = info.lastModified();
|
||||
m_fragment_file = fragmentSource;
|
||||
#if QT_CONFIG(filesystemwatcher)
|
||||
m_watcher.addPath(info.canonicalPath());
|
||||
QObject::connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FragmentToy::fileChanged);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
FragmentToy::~FragmentToy()
|
||||
= default;
|
||||
|
||||
void FragmentToy::draw(const QSize &windowSize)
|
||||
{
|
||||
if (!m_program)
|
||||
initializeOpenGLFunctions();
|
||||
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
if (!m_vao.isCreated())
|
||||
m_vao.create();
|
||||
|
||||
QOpenGLVertexArrayObject::Binder binder(&m_vao);
|
||||
|
||||
if (!m_vertex_buffer.isCreated()) {
|
||||
m_vertex_buffer.create();
|
||||
m_vertex_buffer.bind();
|
||||
m_vertex_buffer.allocate(vertices, sizeof(vertices));
|
||||
m_vertex_buffer.release();
|
||||
}
|
||||
|
||||
if (!m_program) {
|
||||
m_program.reset(new QOpenGLShaderProgram);
|
||||
m_program->create();
|
||||
m_vertex_shader.reset(new QOpenGLShader(QOpenGLShader::Vertex));
|
||||
if (!m_vertex_shader->compileSourceCode(vertex_shader)) {
|
||||
qWarning() << "Failed to compile the vertex shader:" << m_vertex_shader->log();
|
||||
}
|
||||
if (!m_program->addShader(m_vertex_shader.get())) {
|
||||
qWarning() << "Failed to add vertex shader to program:" << m_program->log();
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_fragment_shader && m_recompile_shaders) {
|
||||
QByteArray data;
|
||||
if (m_fragment_file.size()) {
|
||||
QFile file(m_fragment_file);
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
data = file.readAll();
|
||||
} else {
|
||||
qWarning() << "Failed to load input file, falling back to default";
|
||||
data = QByteArray::fromRawData(fragment_shader, sizeof(fragment_shader));
|
||||
}
|
||||
} else {
|
||||
QFile qrcFile(":/background.frag");
|
||||
if (qrcFile.open(QIODevice::ReadOnly))
|
||||
data = qrcFile.readAll();
|
||||
else
|
||||
data = QByteArray::fromRawData(fragment_shader, sizeof(fragment_shader));
|
||||
}
|
||||
if (data.size()) {
|
||||
m_fragment_shader.reset(new QOpenGLShader(QOpenGLShader::Fragment));
|
||||
if (!m_fragment_shader->compileSourceCode(data)) {
|
||||
qWarning() << "Failed to compile fragment shader:" << m_fragment_shader->log();
|
||||
m_fragment_shader.reset(nullptr);
|
||||
}
|
||||
} else {
|
||||
qWarning() << "Unknown error, no fragment shader";
|
||||
}
|
||||
|
||||
if (m_fragment_shader) {
|
||||
if (!m_program->addShader(m_fragment_shader.get())) {
|
||||
qWarning() << "Failed to add fragment shader to program:" << m_program->log();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_recompile_shaders) {
|
||||
m_recompile_shaders = false;
|
||||
|
||||
if (m_program->link()) {
|
||||
m_vertex_coord_pos = m_program->attributeLocation("vertexCoord");
|
||||
} else {
|
||||
qWarning() << "Failed to link shader program" << m_program->log();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!m_program->isLinked())
|
||||
return;
|
||||
|
||||
m_program->bind();
|
||||
|
||||
m_vertex_buffer.bind();
|
||||
m_program->setAttributeBuffer("vertexCoord", GL_FLOAT, 0, 3, 0);
|
||||
m_program->enableAttributeArray("vertexCoord");
|
||||
m_vertex_buffer.release();
|
||||
|
||||
m_program->setUniformValue("currentTime", (uint) QDateTime::currentMSecsSinceEpoch());
|
||||
m_program->setUniformValue("windowSize", windowSize);
|
||||
|
||||
QOpenGLContext::currentContext()->functions()->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
m_program->release();
|
||||
}
|
||||
|
||||
void FragmentToy::fileChanged(const QString &path)
|
||||
{
|
||||
Q_UNUSED(path);
|
||||
if (QFile::exists(m_fragment_file)) {
|
||||
QFileInfo fragment_source(m_fragment_file);
|
||||
if (fragment_source.lastModified() > m_fragment_file_last_modified) {
|
||||
m_fragment_file_last_modified = fragment_source.lastModified();
|
||||
m_recompile_shaders = true;
|
||||
if (m_program) {
|
||||
m_program->removeShader(m_fragment_shader.get());
|
||||
m_fragment_shader.reset(nullptr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_recompile_shaders = true;
|
||||
if (m_program) {
|
||||
m_program->removeShader(m_fragment_shader.get());
|
||||
m_fragment_shader.reset(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef FRAGMENT_TOY_H
|
||||
#define FRAGMENT_TOY_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QFile>
|
||||
#include <QDateTime>
|
||||
#if QT_CONFIG(filesystemwatcher)
|
||||
#include <QFileSystemWatcher>
|
||||
#endif
|
||||
#include <QOpenGLVertexArrayObject>
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QOpenGLFunctions>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class FragmentToy : public QObject, protected QOpenGLFunctions
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FragmentToy(const QString &fragmentSource, QObject *parent = nullptr);
|
||||
~FragmentToy();
|
||||
|
||||
void draw(const QSize &windowSize);
|
||||
|
||||
private:
|
||||
void fileChanged(const QString &path);
|
||||
bool m_recompile_shaders;
|
||||
#if QT_CONFIG(filesystemwatcher)
|
||||
QFileSystemWatcher m_watcher;
|
||||
#endif
|
||||
QString m_fragment_file;
|
||||
QDateTime m_fragment_file_last_modified;
|
||||
|
||||
std::unique_ptr<QOpenGLShaderProgram> m_program;
|
||||
std::unique_ptr<QOpenGLShader> m_vertex_shader;
|
||||
std::unique_ptr<QOpenGLShader> m_fragment_shader;
|
||||
QOpenGLVertexArrayObject m_vao;
|
||||
QOpenGLBuffer m_vertex_buffer;
|
||||
GLuint m_vertex_coord_pos;
|
||||
};
|
||||
|
||||
#endif //FRAGMENT_TOY_H
|
154
tests/manual/examples/opengl/qopenglwindow/main.cpp
Normal file
154
tests/manual/examples/opengl/qopenglwindow/main.cpp
Normal file
@ -0,0 +1,154 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QOpenGLWindow>
|
||||
#include <QScreen>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QGuiApplication>
|
||||
#include <QMatrix4x4>
|
||||
#include <QStaticText>
|
||||
#include <QKeyEvent>
|
||||
|
||||
#include "background_renderer.h"
|
||||
|
||||
static QPainterPath painterPathForTriangle()
|
||||
{
|
||||
static const QPointF bottomLeft(-1.0, -1.0);
|
||||
static const QPointF top(0.0, 1.0);
|
||||
static const QPointF bottomRight(1.0, -1.0);
|
||||
|
||||
QPainterPath path(bottomLeft);
|
||||
path.lineTo(top);
|
||||
path.lineTo(bottomRight);
|
||||
path.closeSubpath();
|
||||
return path;
|
||||
}
|
||||
|
||||
class OpenGLWindow : public QOpenGLWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OpenGLWindow();
|
||||
|
||||
protected:
|
||||
void paintGL() override;
|
||||
void resizeGL(int w, int h) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
|
||||
private:
|
||||
void setAnimating(bool enabled);
|
||||
|
||||
QMatrix4x4 m_window_normalised_matrix;
|
||||
QMatrix4x4 m_window_painter_matrix;
|
||||
QMatrix4x4 m_projection;
|
||||
QMatrix4x4 m_view;
|
||||
QMatrix4x4 m_model_triangle;
|
||||
QMatrix4x4 m_model_text;
|
||||
|
||||
FragmentToy m_fragment_toy;
|
||||
QStaticText m_text_layout;
|
||||
bool m_animate;
|
||||
};
|
||||
|
||||
// Use NoPartialUpdate. This means that all the rendering goes directly to
|
||||
// the window surface, no additional framebuffer object stands in the
|
||||
// middle. This is fine since we will clear the entire framebuffer on each
|
||||
// paint. Under the hood this means that the behavior is equivalent to the
|
||||
// manual makeCurrent - perform OpenGL calls - swapBuffers loop that is
|
||||
// typical in pure QWindow-based applications.
|
||||
OpenGLWindow::OpenGLWindow()
|
||||
: QOpenGLWindow(QOpenGLWindow::NoPartialUpdate)
|
||||
, m_fragment_toy("./background.frag")
|
||||
, m_text_layout("The triangle and this text is rendered with QPainter")
|
||||
, m_animate(true)
|
||||
{
|
||||
setGeometry(300, 300, 500, 500);
|
||||
|
||||
m_view.lookAt(QVector3D(3,1,1),
|
||||
QVector3D(0,0,0),
|
||||
QVector3D(0,1,0));
|
||||
|
||||
setAnimating(m_animate);
|
||||
}
|
||||
|
||||
void OpenGLWindow::paintGL()
|
||||
{
|
||||
m_fragment_toy.draw(size());
|
||||
|
||||
QPainter p(this);
|
||||
p.setWorldTransform(m_window_normalised_matrix.toTransform());
|
||||
|
||||
QMatrix4x4 mvp = m_projection * m_view * m_model_triangle;
|
||||
p.setTransform(mvp.toTransform(), true);
|
||||
|
||||
p.fillPath(painterPathForTriangle(), QBrush(QGradient(QGradient::NightFade)));
|
||||
|
||||
QTransform text_transform = (m_window_painter_matrix * m_view * m_model_text).toTransform();
|
||||
p.setTransform(text_transform, false);
|
||||
p.setPen(QPen(Qt::black));
|
||||
m_text_layout.prepare(text_transform);
|
||||
qreal x = - (m_text_layout.size().width() / 2);
|
||||
qreal y = 0;
|
||||
p.drawStaticText(x, y, m_text_layout);
|
||||
|
||||
m_model_triangle.rotate(-1, 0, 1, 0);
|
||||
m_model_text.rotate(1, 0, 1, 0);
|
||||
}
|
||||
|
||||
void OpenGLWindow::resizeGL(int w, int h)
|
||||
{
|
||||
m_window_normalised_matrix.setToIdentity();
|
||||
m_window_normalised_matrix.translate(w / 2.0, h / 2.0);
|
||||
m_window_normalised_matrix.scale(w / 2.0, -h / 2.0);
|
||||
|
||||
m_window_painter_matrix.setToIdentity();
|
||||
m_window_painter_matrix.translate(w / 2.0, h / 2.0);
|
||||
|
||||
m_text_layout.setTextWidth(std::max(w * 0.2, 80.0));
|
||||
|
||||
m_projection.setToIdentity();
|
||||
m_projection.perspective(45.f, qreal(w) / qreal(h), 0.1f, 100.f);
|
||||
}
|
||||
|
||||
void OpenGLWindow::keyPressEvent(QKeyEvent *e)
|
||||
{
|
||||
if (e->key() == Qt::Key_P) { // pause
|
||||
m_animate = !m_animate;
|
||||
setAnimating(m_animate);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLWindow::setAnimating(bool enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
// Animate continuously, throttled by the blocking swapBuffers() call the
|
||||
// QOpenGLWindow internally executes after each paint. Once that is done
|
||||
// (frameSwapped signal is emitted), we schedule a new update. This
|
||||
// obviously assumes that the swap interval (see
|
||||
// QSurfaceFormat::setSwapInterval()) is non-zero.
|
||||
connect(this, &QOpenGLWindow::frameSwapped,
|
||||
this, QOverload<>::of(&QPaintDeviceWindow::update));
|
||||
update();
|
||||
} else {
|
||||
disconnect(this, &QOpenGLWindow::frameSwapped,
|
||||
this, QOverload<>::of(&QPaintDeviceWindow::update));
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
OpenGLWindow window;
|
||||
QSurfaceFormat fmt;
|
||||
fmt.setDepthBufferSize(24);
|
||||
fmt.setStencilBufferSize(8);
|
||||
window.setFormat(fmt);
|
||||
window.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
#include "main.moc"
|
16
tests/manual/examples/opengl/qopenglwindow/qopenglwindow.pro
Normal file
16
tests/manual/examples/opengl/qopenglwindow/qopenglwindow.pro
Normal file
@ -0,0 +1,16 @@
|
||||
TEMPLATE = app
|
||||
TARGET = qopenglwindow
|
||||
INCLUDEPATH += .
|
||||
QT += opengl
|
||||
|
||||
RESOURCES += shaders.qrc
|
||||
|
||||
SOURCES += \
|
||||
main.cpp \
|
||||
background_renderer.cpp
|
||||
|
||||
HEADERS += \
|
||||
background_renderer.h
|
||||
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/opengl/qopenglwindow
|
||||
INSTALLS += target
|
5
tests/manual/examples/opengl/qopenglwindow/shaders.qrc
Normal file
5
tests/manual/examples/opengl/qopenglwindow/shaders.qrc
Normal file
@ -0,0 +1,5 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
<file>background.frag</file>
|
||||
</qresource>
|
||||
</RCC>
|
@ -0,0 +1,50 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(hellovulkantexture LANGUAGES CXX)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/vulkan/hellovulkantexture")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(hellovulkantexture
|
||||
hellovulkantexture.cpp hellovulkantexture.h
|
||||
main.cpp
|
||||
)
|
||||
|
||||
set_target_properties(hellovulkantexture PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
|
||||
target_link_libraries(hellovulkantexture PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
)
|
||||
|
||||
# Resources:
|
||||
set(hellovulkantexture_resource_files
|
||||
"qt256.png"
|
||||
"texture_frag.spv"
|
||||
"texture_vert.spv"
|
||||
)
|
||||
|
||||
qt_add_resources(hellovulkantexture "hellovulkantexture"
|
||||
PREFIX
|
||||
"/"
|
||||
FILES
|
||||
${hellovulkantexture_resource_files}
|
||||
)
|
||||
|
||||
install(TARGETS hellovulkantexture
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
@ -0,0 +1,792 @@
|
||||
// Copyright (C) 2017 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "hellovulkantexture.h"
|
||||
#include <QVulkanFunctions>
|
||||
#include <QCoreApplication>
|
||||
#include <QFile>
|
||||
|
||||
// Use a triangle strip to get a quad.
|
||||
//
|
||||
// Note that the vertex data and the projection matrix assume OpenGL. With
|
||||
// Vulkan Y is negated in clip space and the near/far plane is at 0/1 instead
|
||||
// of -1/1. These will be corrected for by an extra transformation when
|
||||
// calculating the modelview-projection matrix.
|
||||
static float vertexData[] = { // Y up, front = CW
|
||||
// x, y, z, u, v
|
||||
-1, -1, 0, 0, 1,
|
||||
-1, 1, 0, 0, 0,
|
||||
1, -1, 0, 1, 1,
|
||||
1, 1, 0, 1, 0
|
||||
};
|
||||
|
||||
static const int UNIFORM_DATA_SIZE = 16 * sizeof(float);
|
||||
|
||||
static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
|
||||
{
|
||||
return (v + byteAlign - 1) & ~(byteAlign - 1);
|
||||
}
|
||||
|
||||
QVulkanWindowRenderer *VulkanWindow::createRenderer()
|
||||
{
|
||||
return new VulkanRenderer(this);
|
||||
}
|
||||
|
||||
VulkanRenderer::VulkanRenderer(QVulkanWindow *w)
|
||||
: m_window(w)
|
||||
{
|
||||
}
|
||||
|
||||
VkShaderModule VulkanRenderer::createShader(const QString &name)
|
||||
{
|
||||
QFile file(name);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qWarning("Failed to read shader %s", qPrintable(name));
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
QByteArray blob = file.readAll();
|
||||
file.close();
|
||||
|
||||
VkShaderModuleCreateInfo shaderInfo;
|
||||
memset(&shaderInfo, 0, sizeof(shaderInfo));
|
||||
shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
shaderInfo.codeSize = blob.size();
|
||||
shaderInfo.pCode = reinterpret_cast<const uint32_t *>(blob.constData());
|
||||
VkShaderModule shaderModule;
|
||||
VkResult err = m_devFuncs->vkCreateShaderModule(m_window->device(), &shaderInfo, nullptr, &shaderModule);
|
||||
if (err != VK_SUCCESS) {
|
||||
qWarning("Failed to create shader module: %d", err);
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return shaderModule;
|
||||
}
|
||||
|
||||
bool VulkanRenderer::createTexture(const QString &name)
|
||||
{
|
||||
QImage img(name);
|
||||
if (img.isNull()) {
|
||||
qWarning("Failed to load image %s", qPrintable(name));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert to byte ordered RGBA8. Use premultiplied alpha, see pColorBlendState in the pipeline.
|
||||
img = img.convertToFormat(QImage::Format_RGBA8888_Premultiplied);
|
||||
|
||||
QVulkanFunctions *f = m_window->vulkanInstance()->functions();
|
||||
VkDevice dev = m_window->device();
|
||||
|
||||
const bool srgb = QCoreApplication::arguments().contains(QStringLiteral("--srgb"));
|
||||
if (srgb)
|
||||
qDebug("sRGB swapchain was requested, making texture sRGB too");
|
||||
|
||||
m_texFormat = srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
|
||||
|
||||
// Now we can either map and copy the image data directly, or have to go
|
||||
// through a staging buffer to copy and convert into the internal optimal
|
||||
// tiling format.
|
||||
VkFormatProperties props;
|
||||
f->vkGetPhysicalDeviceFormatProperties(m_window->physicalDevice(), m_texFormat, &props);
|
||||
const bool canSampleLinear = (props.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
|
||||
const bool canSampleOptimal = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
|
||||
if (!canSampleLinear && !canSampleOptimal) {
|
||||
qWarning("Neither linear nor optimal image sampling is supported for RGBA8");
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool alwaysStage = qEnvironmentVariableIntValue("QT_VK_FORCE_STAGE_TEX");
|
||||
|
||||
if (canSampleLinear && !alwaysStage) {
|
||||
if (!createTextureImage(img.size(), &m_texImage, &m_texMem,
|
||||
VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
m_window->hostVisibleMemoryIndex()))
|
||||
return false;
|
||||
|
||||
if (!writeLinearImage(img, m_texImage, m_texMem))
|
||||
return false;
|
||||
|
||||
m_texLayoutPending = true;
|
||||
} else {
|
||||
if (!createTextureImage(img.size(), &m_texStaging, &m_texStagingMem,
|
||||
VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
|
||||
m_window->hostVisibleMemoryIndex()))
|
||||
return false;
|
||||
|
||||
if (!createTextureImage(img.size(), &m_texImage, &m_texMem,
|
||||
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
m_window->deviceLocalMemoryIndex()))
|
||||
return false;
|
||||
|
||||
if (!writeLinearImage(img, m_texStaging, m_texStagingMem))
|
||||
return false;
|
||||
|
||||
m_texStagingPending = true;
|
||||
}
|
||||
|
||||
VkImageViewCreateInfo viewInfo;
|
||||
memset(&viewInfo, 0, sizeof(viewInfo));
|
||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
viewInfo.image = m_texImage;
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.format = m_texFormat;
|
||||
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
|
||||
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
|
||||
viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
|
||||
viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
|
||||
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
viewInfo.subresourceRange.levelCount = viewInfo.subresourceRange.layerCount = 1;
|
||||
|
||||
VkResult err = m_devFuncs->vkCreateImageView(dev, &viewInfo, nullptr, &m_texView);
|
||||
if (err != VK_SUCCESS) {
|
||||
qWarning("Failed to create image view for texture: %d", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_texSize = img.size();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanRenderer::createTextureImage(const QSize &size, VkImage *image, VkDeviceMemory *mem,
|
||||
VkImageTiling tiling, VkImageUsageFlags usage, uint32_t memIndex)
|
||||
{
|
||||
VkDevice dev = m_window->device();
|
||||
|
||||
VkImageCreateInfo imageInfo;
|
||||
memset(&imageInfo, 0, sizeof(imageInfo));
|
||||
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
imageInfo.format = m_texFormat;
|
||||
imageInfo.extent.width = size.width();
|
||||
imageInfo.extent.height = size.height();
|
||||
imageInfo.extent.depth = 1;
|
||||
imageInfo.mipLevels = 1;
|
||||
imageInfo.arrayLayers = 1;
|
||||
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
imageInfo.tiling = tiling;
|
||||
imageInfo.usage = usage;
|
||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
||||
|
||||
VkResult err = m_devFuncs->vkCreateImage(dev, &imageInfo, nullptr, image);
|
||||
if (err != VK_SUCCESS) {
|
||||
qWarning("Failed to create linear image for texture: %d", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
VkMemoryRequirements memReq;
|
||||
m_devFuncs->vkGetImageMemoryRequirements(dev, *image, &memReq);
|
||||
|
||||
if (!(memReq.memoryTypeBits & (1 << memIndex))) {
|
||||
VkPhysicalDeviceMemoryProperties physDevMemProps;
|
||||
m_window->vulkanInstance()->functions()->vkGetPhysicalDeviceMemoryProperties(m_window->physicalDevice(), &physDevMemProps);
|
||||
for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) {
|
||||
if (!(memReq.memoryTypeBits & (1 << i)))
|
||||
continue;
|
||||
memIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
VkMemoryAllocateInfo allocInfo = {
|
||||
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
nullptr,
|
||||
memReq.size,
|
||||
memIndex
|
||||
};
|
||||
qDebug("allocating %u bytes for texture image", uint32_t(memReq.size));
|
||||
|
||||
err = m_devFuncs->vkAllocateMemory(dev, &allocInfo, nullptr, mem);
|
||||
if (err != VK_SUCCESS) {
|
||||
qWarning("Failed to allocate memory for linear image: %d", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
err = m_devFuncs->vkBindImageMemory(dev, *image, *mem, 0);
|
||||
if (err != VK_SUCCESS) {
|
||||
qWarning("Failed to bind linear image memory: %d", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanRenderer::writeLinearImage(const QImage &img, VkImage image, VkDeviceMemory memory)
|
||||
{
|
||||
VkDevice dev = m_window->device();
|
||||
|
||||
VkImageSubresource subres = {
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
0, // mip level
|
||||
0
|
||||
};
|
||||
VkSubresourceLayout layout;
|
||||
m_devFuncs->vkGetImageSubresourceLayout(dev, image, &subres, &layout);
|
||||
|
||||
uchar *p;
|
||||
VkResult err = m_devFuncs->vkMapMemory(dev, memory, layout.offset, layout.size, 0, reinterpret_cast<void **>(&p));
|
||||
if (err != VK_SUCCESS) {
|
||||
qWarning("Failed to map memory for linear image: %d", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int y = 0; y < img.height(); ++y) {
|
||||
const uchar *line = img.constScanLine(y);
|
||||
memcpy(p, line, img.width() * 4);
|
||||
p += layout.rowPitch;
|
||||
}
|
||||
|
||||
m_devFuncs->vkUnmapMemory(dev, memory);
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanRenderer::ensureTexture()
|
||||
{
|
||||
if (!m_texLayoutPending && !m_texStagingPending)
|
||||
return;
|
||||
|
||||
Q_ASSERT(m_texLayoutPending != m_texStagingPending);
|
||||
VkCommandBuffer cb = m_window->currentCommandBuffer();
|
||||
|
||||
VkImageMemoryBarrier barrier;
|
||||
memset(&barrier, 0, sizeof(barrier));
|
||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
barrier.subresourceRange.levelCount = barrier.subresourceRange.layerCount = 1;
|
||||
|
||||
if (m_texLayoutPending) {
|
||||
m_texLayoutPending = false;
|
||||
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
barrier.image = m_texImage;
|
||||
|
||||
m_devFuncs->vkCmdPipelineBarrier(cb,
|
||||
VK_PIPELINE_STAGE_HOST_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
0, 0, nullptr, 0, nullptr,
|
||||
1, &barrier);
|
||||
} else {
|
||||
m_texStagingPending = false;
|
||||
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
barrier.image = m_texStaging;
|
||||
m_devFuncs->vkCmdPipelineBarrier(cb,
|
||||
VK_PIPELINE_STAGE_HOST_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
0, 0, nullptr, 0, nullptr,
|
||||
1, &barrier);
|
||||
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
barrier.srcAccessMask = 0;
|
||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
barrier.image = m_texImage;
|
||||
m_devFuncs->vkCmdPipelineBarrier(cb,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
0, 0, nullptr, 0, nullptr,
|
||||
1, &barrier);
|
||||
|
||||
VkImageCopy copyInfo;
|
||||
memset(©Info, 0, sizeof(copyInfo));
|
||||
copyInfo.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
copyInfo.srcSubresource.layerCount = 1;
|
||||
copyInfo.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
copyInfo.dstSubresource.layerCount = 1;
|
||||
copyInfo.extent.width = m_texSize.width();
|
||||
copyInfo.extent.height = m_texSize.height();
|
||||
copyInfo.extent.depth = 1;
|
||||
m_devFuncs->vkCmdCopyImage(cb, m_texStaging, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
m_texImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Info);
|
||||
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
barrier.image = m_texImage;
|
||||
m_devFuncs->vkCmdPipelineBarrier(cb,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
0, 0, nullptr, 0, nullptr,
|
||||
1, &barrier);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanRenderer::initResources()
|
||||
{
|
||||
qDebug("initResources");
|
||||
|
||||
VkDevice dev = m_window->device();
|
||||
m_devFuncs = m_window->vulkanInstance()->deviceFunctions(dev);
|
||||
|
||||
// The setup is similar to hellovulkantriangle. The difference is the
|
||||
// presence of a second vertex attribute (texcoord), a sampler, and that we
|
||||
// need blending.
|
||||
|
||||
const int concurrentFrameCount = m_window->concurrentFrameCount();
|
||||
const VkPhysicalDeviceLimits *pdevLimits = &m_window->physicalDeviceProperties()->limits;
|
||||
const VkDeviceSize uniAlign = pdevLimits->minUniformBufferOffsetAlignment;
|
||||
qDebug("uniform buffer offset alignment is %u", (uint) uniAlign);
|
||||
VkBufferCreateInfo bufInfo;
|
||||
memset(&bufInfo, 0, sizeof(bufInfo));
|
||||
bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
// Our internal layout is vertex, uniform, uniform, ... with each uniform buffer start offset aligned to uniAlign.
|
||||
const VkDeviceSize vertexAllocSize = aligned(sizeof(vertexData), uniAlign);
|
||||
const VkDeviceSize uniformAllocSize = aligned(UNIFORM_DATA_SIZE, uniAlign);
|
||||
bufInfo.size = vertexAllocSize + concurrentFrameCount * uniformAllocSize;
|
||||
bufInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
|
||||
|
||||
VkResult err = m_devFuncs->vkCreateBuffer(dev, &bufInfo, nullptr, &m_buf);
|
||||
if (err != VK_SUCCESS)
|
||||
qFatal("Failed to create buffer: %d", err);
|
||||
|
||||
VkMemoryRequirements memReq;
|
||||
m_devFuncs->vkGetBufferMemoryRequirements(dev, m_buf, &memReq);
|
||||
|
||||
VkMemoryAllocateInfo memAllocInfo = {
|
||||
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
nullptr,
|
||||
memReq.size,
|
||||
m_window->hostVisibleMemoryIndex()
|
||||
};
|
||||
|
||||
err = m_devFuncs->vkAllocateMemory(dev, &memAllocInfo, nullptr, &m_bufMem);
|
||||
if (err != VK_SUCCESS)
|
||||
qFatal("Failed to allocate memory: %d", err);
|
||||
|
||||
err = m_devFuncs->vkBindBufferMemory(dev, m_buf, m_bufMem, 0);
|
||||
if (err != VK_SUCCESS)
|
||||
qFatal("Failed to bind buffer memory: %d", err);
|
||||
|
||||
quint8 *p;
|
||||
err = m_devFuncs->vkMapMemory(dev, m_bufMem, 0, memReq.size, 0, reinterpret_cast<void **>(&p));
|
||||
if (err != VK_SUCCESS)
|
||||
qFatal("Failed to map memory: %d", err);
|
||||
memcpy(p, vertexData, sizeof(vertexData));
|
||||
QMatrix4x4 ident;
|
||||
memset(m_uniformBufInfo, 0, sizeof(m_uniformBufInfo));
|
||||
for (int i = 0; i < concurrentFrameCount; ++i) {
|
||||
const VkDeviceSize offset = vertexAllocSize + i * uniformAllocSize;
|
||||
memcpy(p + offset, ident.constData(), 16 * sizeof(float));
|
||||
m_uniformBufInfo[i].buffer = m_buf;
|
||||
m_uniformBufInfo[i].offset = offset;
|
||||
m_uniformBufInfo[i].range = uniformAllocSize;
|
||||
}
|
||||
m_devFuncs->vkUnmapMemory(dev, m_bufMem);
|
||||
|
||||
VkVertexInputBindingDescription vertexBindingDesc = {
|
||||
0, // binding
|
||||
5 * sizeof(float),
|
||||
VK_VERTEX_INPUT_RATE_VERTEX
|
||||
};
|
||||
VkVertexInputAttributeDescription vertexAttrDesc[] = {
|
||||
{ // position
|
||||
0, // location
|
||||
0, // binding
|
||||
VK_FORMAT_R32G32B32_SFLOAT,
|
||||
0
|
||||
},
|
||||
{ // texcoord
|
||||
1,
|
||||
0,
|
||||
VK_FORMAT_R32G32_SFLOAT,
|
||||
3 * sizeof(float)
|
||||
}
|
||||
};
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertexInputInfo;
|
||||
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
vertexInputInfo.pNext = nullptr;
|
||||
vertexInputInfo.flags = 0;
|
||||
vertexInputInfo.vertexBindingDescriptionCount = 1;
|
||||
vertexInputInfo.pVertexBindingDescriptions = &vertexBindingDesc;
|
||||
vertexInputInfo.vertexAttributeDescriptionCount = 2;
|
||||
vertexInputInfo.pVertexAttributeDescriptions = vertexAttrDesc;
|
||||
|
||||
// Sampler.
|
||||
VkSamplerCreateInfo samplerInfo;
|
||||
memset(&samplerInfo, 0, sizeof(samplerInfo));
|
||||
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||
samplerInfo.magFilter = VK_FILTER_NEAREST;
|
||||
samplerInfo.minFilter = VK_FILTER_NEAREST;
|
||||
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samplerInfo.maxAnisotropy = 1.0f;
|
||||
err = m_devFuncs->vkCreateSampler(dev, &samplerInfo, nullptr, &m_sampler);
|
||||
if (err != VK_SUCCESS)
|
||||
qFatal("Failed to create sampler: %d", err);
|
||||
|
||||
// Texture.
|
||||
if (!createTexture(QStringLiteral(":/qt256.png")))
|
||||
qFatal("Failed to create texture");
|
||||
|
||||
// Set up descriptor set and its layout.
|
||||
VkDescriptorPoolSize descPoolSizes[2] = {
|
||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uint32_t(concurrentFrameCount) },
|
||||
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, uint32_t(concurrentFrameCount) }
|
||||
};
|
||||
VkDescriptorPoolCreateInfo descPoolInfo;
|
||||
memset(&descPoolInfo, 0, sizeof(descPoolInfo));
|
||||
descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
descPoolInfo.maxSets = concurrentFrameCount;
|
||||
descPoolInfo.poolSizeCount = 2;
|
||||
descPoolInfo.pPoolSizes = descPoolSizes;
|
||||
err = m_devFuncs->vkCreateDescriptorPool(dev, &descPoolInfo, nullptr, &m_descPool);
|
||||
if (err != VK_SUCCESS)
|
||||
qFatal("Failed to create descriptor pool: %d", err);
|
||||
|
||||
VkDescriptorSetLayoutBinding layoutBinding[2] =
|
||||
{
|
||||
{
|
||||
0, // binding
|
||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||
1, // descriptorCount
|
||||
VK_SHADER_STAGE_VERTEX_BIT,
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
1, // binding
|
||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
1, // descriptorCount
|
||||
VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
nullptr
|
||||
}
|
||||
};
|
||||
VkDescriptorSetLayoutCreateInfo descLayoutInfo = {
|
||||
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
2, // bindingCount
|
||||
layoutBinding
|
||||
};
|
||||
err = m_devFuncs->vkCreateDescriptorSetLayout(dev, &descLayoutInfo, nullptr, &m_descSetLayout);
|
||||
if (err != VK_SUCCESS)
|
||||
qFatal("Failed to create descriptor set layout: %d", err);
|
||||
|
||||
for (int i = 0; i < concurrentFrameCount; ++i) {
|
||||
VkDescriptorSetAllocateInfo descSetAllocInfo = {
|
||||
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||
nullptr,
|
||||
m_descPool,
|
||||
1,
|
||||
&m_descSetLayout
|
||||
};
|
||||
err = m_devFuncs->vkAllocateDescriptorSets(dev, &descSetAllocInfo, &m_descSet[i]);
|
||||
if (err != VK_SUCCESS)
|
||||
qFatal("Failed to allocate descriptor set: %d", err);
|
||||
|
||||
VkWriteDescriptorSet descWrite[2];
|
||||
memset(descWrite, 0, sizeof(descWrite));
|
||||
descWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
descWrite[0].dstSet = m_descSet[i];
|
||||
descWrite[0].dstBinding = 0;
|
||||
descWrite[0].descriptorCount = 1;
|
||||
descWrite[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
descWrite[0].pBufferInfo = &m_uniformBufInfo[i];
|
||||
|
||||
VkDescriptorImageInfo descImageInfo = {
|
||||
m_sampler,
|
||||
m_texView,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
|
||||
};
|
||||
|
||||
descWrite[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
descWrite[1].dstSet = m_descSet[i];
|
||||
descWrite[1].dstBinding = 1;
|
||||
descWrite[1].descriptorCount = 1;
|
||||
descWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
descWrite[1].pImageInfo = &descImageInfo;
|
||||
|
||||
m_devFuncs->vkUpdateDescriptorSets(dev, 2, descWrite, 0, nullptr);
|
||||
}
|
||||
|
||||
// Pipeline cache
|
||||
VkPipelineCacheCreateInfo pipelineCacheInfo;
|
||||
memset(&pipelineCacheInfo, 0, sizeof(pipelineCacheInfo));
|
||||
pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
|
||||
err = m_devFuncs->vkCreatePipelineCache(dev, &pipelineCacheInfo, nullptr, &m_pipelineCache);
|
||||
if (err != VK_SUCCESS)
|
||||
qFatal("Failed to create pipeline cache: %d", err);
|
||||
|
||||
// Pipeline layout
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutInfo;
|
||||
memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo));
|
||||
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
pipelineLayoutInfo.setLayoutCount = 1;
|
||||
pipelineLayoutInfo.pSetLayouts = &m_descSetLayout;
|
||||
err = m_devFuncs->vkCreatePipelineLayout(dev, &pipelineLayoutInfo, nullptr, &m_pipelineLayout);
|
||||
if (err != VK_SUCCESS)
|
||||
qFatal("Failed to create pipeline layout: %d", err);
|
||||
|
||||
// Shaders
|
||||
VkShaderModule vertShaderModule = createShader(QStringLiteral(":/texture_vert.spv"));
|
||||
VkShaderModule fragShaderModule = createShader(QStringLiteral(":/texture_frag.spv"));
|
||||
|
||||
// Graphics pipeline
|
||||
VkGraphicsPipelineCreateInfo pipelineInfo;
|
||||
memset(&pipelineInfo, 0, sizeof(pipelineInfo));
|
||||
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
|
||||
VkPipelineShaderStageCreateInfo shaderStages[2] = {
|
||||
{
|
||||
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
VK_SHADER_STAGE_VERTEX_BIT,
|
||||
vertShaderModule,
|
||||
"main",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
fragShaderModule,
|
||||
"main",
|
||||
nullptr
|
||||
}
|
||||
};
|
||||
pipelineInfo.stageCount = 2;
|
||||
pipelineInfo.pStages = shaderStages;
|
||||
|
||||
pipelineInfo.pVertexInputState = &vertexInputInfo;
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo ia;
|
||||
memset(&ia, 0, sizeof(ia));
|
||||
ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
||||
pipelineInfo.pInputAssemblyState = &ia;
|
||||
|
||||
// The viewport and scissor will be set dynamically via vkCmdSetViewport/Scissor.
|
||||
// This way the pipeline does not need to be touched when resizing the window.
|
||||
VkPipelineViewportStateCreateInfo vp;
|
||||
memset(&vp, 0, sizeof(vp));
|
||||
vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
vp.viewportCount = 1;
|
||||
vp.scissorCount = 1;
|
||||
pipelineInfo.pViewportState = &vp;
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rs;
|
||||
memset(&rs, 0, sizeof(rs));
|
||||
rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
rs.polygonMode = VK_POLYGON_MODE_FILL;
|
||||
rs.cullMode = VK_CULL_MODE_BACK_BIT;
|
||||
rs.frontFace = VK_FRONT_FACE_CLOCKWISE;
|
||||
rs.lineWidth = 1.0f;
|
||||
pipelineInfo.pRasterizationState = &rs;
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo ms;
|
||||
memset(&ms, 0, sizeof(ms));
|
||||
ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
pipelineInfo.pMultisampleState = &ms;
|
||||
|
||||
VkPipelineDepthStencilStateCreateInfo ds;
|
||||
memset(&ds, 0, sizeof(ds));
|
||||
ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
||||
ds.depthTestEnable = VK_TRUE;
|
||||
ds.depthWriteEnable = VK_TRUE;
|
||||
ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
|
||||
pipelineInfo.pDepthStencilState = &ds;
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo cb;
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
// assume pre-multiplied alpha, blend, write out all of rgba
|
||||
VkPipelineColorBlendAttachmentState att;
|
||||
memset(&att, 0, sizeof(att));
|
||||
att.colorWriteMask = 0xF;
|
||||
att.blendEnable = VK_TRUE;
|
||||
att.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
att.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
att.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
att.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
att.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
att.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
cb.attachmentCount = 1;
|
||||
cb.pAttachments = &att;
|
||||
pipelineInfo.pColorBlendState = &cb;
|
||||
|
||||
VkDynamicState dynEnable[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
|
||||
VkPipelineDynamicStateCreateInfo dyn;
|
||||
memset(&dyn, 0, sizeof(dyn));
|
||||
dyn.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||
dyn.dynamicStateCount = sizeof(dynEnable) / sizeof(VkDynamicState);
|
||||
dyn.pDynamicStates = dynEnable;
|
||||
pipelineInfo.pDynamicState = &dyn;
|
||||
|
||||
pipelineInfo.layout = m_pipelineLayout;
|
||||
pipelineInfo.renderPass = m_window->defaultRenderPass();
|
||||
|
||||
err = m_devFuncs->vkCreateGraphicsPipelines(dev, m_pipelineCache, 1, &pipelineInfo, nullptr, &m_pipeline);
|
||||
if (err != VK_SUCCESS)
|
||||
qFatal("Failed to create graphics pipeline: %d", err);
|
||||
|
||||
if (vertShaderModule)
|
||||
m_devFuncs->vkDestroyShaderModule(dev, vertShaderModule, nullptr);
|
||||
if (fragShaderModule)
|
||||
m_devFuncs->vkDestroyShaderModule(dev, fragShaderModule, nullptr);
|
||||
}
|
||||
|
||||
void VulkanRenderer::initSwapChainResources()
|
||||
{
|
||||
qDebug("initSwapChainResources");
|
||||
|
||||
// Projection matrix
|
||||
m_proj = m_window->clipCorrectionMatrix(); // adjust for Vulkan-OpenGL clip space differences
|
||||
const QSize sz = m_window->swapChainImageSize();
|
||||
m_proj.perspective(45.0f, sz.width() / (float) sz.height(), 0.01f, 100.0f);
|
||||
m_proj.translate(0, 0, -4);
|
||||
}
|
||||
|
||||
void VulkanRenderer::releaseSwapChainResources()
|
||||
{
|
||||
qDebug("releaseSwapChainResources");
|
||||
}
|
||||
|
||||
void VulkanRenderer::releaseResources()
|
||||
{
|
||||
qDebug("releaseResources");
|
||||
|
||||
VkDevice dev = m_window->device();
|
||||
|
||||
if (m_sampler) {
|
||||
m_devFuncs->vkDestroySampler(dev, m_sampler, nullptr);
|
||||
m_sampler = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (m_texStaging) {
|
||||
m_devFuncs->vkDestroyImage(dev, m_texStaging, nullptr);
|
||||
m_texStaging = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (m_texStagingMem) {
|
||||
m_devFuncs->vkFreeMemory(dev, m_texStagingMem, nullptr);
|
||||
m_texStagingMem = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (m_texView) {
|
||||
m_devFuncs->vkDestroyImageView(dev, m_texView, nullptr);
|
||||
m_texView = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (m_texImage) {
|
||||
m_devFuncs->vkDestroyImage(dev, m_texImage, nullptr);
|
||||
m_texImage = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (m_texMem) {
|
||||
m_devFuncs->vkFreeMemory(dev, m_texMem, nullptr);
|
||||
m_texMem = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (m_pipeline) {
|
||||
m_devFuncs->vkDestroyPipeline(dev, m_pipeline, nullptr);
|
||||
m_pipeline = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (m_pipelineLayout) {
|
||||
m_devFuncs->vkDestroyPipelineLayout(dev, m_pipelineLayout, nullptr);
|
||||
m_pipelineLayout = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (m_pipelineCache) {
|
||||
m_devFuncs->vkDestroyPipelineCache(dev, m_pipelineCache, nullptr);
|
||||
m_pipelineCache = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (m_descSetLayout) {
|
||||
m_devFuncs->vkDestroyDescriptorSetLayout(dev, m_descSetLayout, nullptr);
|
||||
m_descSetLayout = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (m_descPool) {
|
||||
m_devFuncs->vkDestroyDescriptorPool(dev, m_descPool, nullptr);
|
||||
m_descPool = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (m_buf) {
|
||||
m_devFuncs->vkDestroyBuffer(dev, m_buf, nullptr);
|
||||
m_buf = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (m_bufMem) {
|
||||
m_devFuncs->vkFreeMemory(dev, m_bufMem, nullptr);
|
||||
m_bufMem = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanRenderer::startNextFrame()
|
||||
{
|
||||
VkDevice dev = m_window->device();
|
||||
VkCommandBuffer cb = m_window->currentCommandBuffer();
|
||||
const QSize sz = m_window->swapChainImageSize();
|
||||
|
||||
// Add the necessary barriers and do the host-linear -> device-optimal copy, if not yet done.
|
||||
ensureTexture();
|
||||
|
||||
VkClearColorValue clearColor = {{ 0, 0, 0, 1 }};
|
||||
VkClearDepthStencilValue clearDS = { 1, 0 };
|
||||
VkClearValue clearValues[2];
|
||||
memset(clearValues, 0, sizeof(clearValues));
|
||||
clearValues[0].color = clearColor;
|
||||
clearValues[1].depthStencil = clearDS;
|
||||
|
||||
VkRenderPassBeginInfo rpBeginInfo;
|
||||
memset(&rpBeginInfo, 0, sizeof(rpBeginInfo));
|
||||
rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
rpBeginInfo.renderPass = m_window->defaultRenderPass();
|
||||
rpBeginInfo.framebuffer = m_window->currentFramebuffer();
|
||||
rpBeginInfo.renderArea.extent.width = sz.width();
|
||||
rpBeginInfo.renderArea.extent.height = sz.height();
|
||||
rpBeginInfo.clearValueCount = 2;
|
||||
rpBeginInfo.pClearValues = clearValues;
|
||||
VkCommandBuffer cmdBuf = m_window->currentCommandBuffer();
|
||||
m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
quint8 *p;
|
||||
VkResult err = m_devFuncs->vkMapMemory(dev, m_bufMem, m_uniformBufInfo[m_window->currentFrame()].offset,
|
||||
UNIFORM_DATA_SIZE, 0, reinterpret_cast<void **>(&p));
|
||||
if (err != VK_SUCCESS)
|
||||
qFatal("Failed to map memory: %d", err);
|
||||
QMatrix4x4 m = m_proj;
|
||||
m.rotate(m_rotation, 0, 0, 1);
|
||||
memcpy(p, m.constData(), 16 * sizeof(float));
|
||||
m_devFuncs->vkUnmapMemory(dev, m_bufMem);
|
||||
|
||||
// Not exactly a real animation system, just advance on every frame for now.
|
||||
m_rotation += 1.0f;
|
||||
|
||||
m_devFuncs->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline);
|
||||
m_devFuncs->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1,
|
||||
&m_descSet[m_window->currentFrame()], 0, nullptr);
|
||||
VkDeviceSize vbOffset = 0;
|
||||
m_devFuncs->vkCmdBindVertexBuffers(cb, 0, 1, &m_buf, &vbOffset);
|
||||
|
||||
VkViewport viewport;
|
||||
viewport.x = viewport.y = 0;
|
||||
viewport.width = sz.width();
|
||||
viewport.height = sz.height();
|
||||
viewport.minDepth = 0;
|
||||
viewport.maxDepth = 1;
|
||||
m_devFuncs->vkCmdSetViewport(cb, 0, 1, &viewport);
|
||||
|
||||
VkRect2D scissor;
|
||||
scissor.offset.x = scissor.offset.y = 0;
|
||||
scissor.extent.width = viewport.width;
|
||||
scissor.extent.height = viewport.height;
|
||||
m_devFuncs->vkCmdSetScissor(cb, 0, 1, &scissor);
|
||||
|
||||
m_devFuncs->vkCmdDraw(cb, 4, 1, 0, 0);
|
||||
|
||||
m_devFuncs->vkCmdEndRenderPass(cmdBuf);
|
||||
|
||||
m_window->frameReady();
|
||||
m_window->requestUpdate(); // render continuously, throttled by the presentation rate
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
// Copyright (C) 2017 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef HELLOVULKANTEXTURE_H
|
||||
#define HELLOVULKANTEXTURE_H
|
||||
|
||||
#include <QVulkanWindow>
|
||||
#include <QImage>
|
||||
|
||||
class VulkanRenderer : public QVulkanWindowRenderer
|
||||
{
|
||||
public:
|
||||
VulkanRenderer(QVulkanWindow *w);
|
||||
|
||||
void initResources() override;
|
||||
void initSwapChainResources() override;
|
||||
void releaseSwapChainResources() override;
|
||||
void releaseResources() override;
|
||||
|
||||
void startNextFrame() override;
|
||||
|
||||
private:
|
||||
VkShaderModule createShader(const QString &name);
|
||||
bool createTexture(const QString &name);
|
||||
bool createTextureImage(const QSize &size, VkImage *image, VkDeviceMemory *mem,
|
||||
VkImageTiling tiling, VkImageUsageFlags usage, uint32_t memIndex);
|
||||
bool writeLinearImage(const QImage &img, VkImage image, VkDeviceMemory memory);
|
||||
void ensureTexture();
|
||||
|
||||
QVulkanWindow *m_window;
|
||||
QVulkanDeviceFunctions *m_devFuncs;
|
||||
|
||||
VkDeviceMemory m_bufMem = VK_NULL_HANDLE;
|
||||
VkBuffer m_buf = VK_NULL_HANDLE;
|
||||
VkDescriptorBufferInfo m_uniformBufInfo[QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT];
|
||||
|
||||
VkDescriptorPool m_descPool = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout m_descSetLayout = VK_NULL_HANDLE;
|
||||
VkDescriptorSet m_descSet[QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT];
|
||||
|
||||
VkPipelineCache m_pipelineCache = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
|
||||
VkPipeline m_pipeline = VK_NULL_HANDLE;
|
||||
|
||||
VkSampler m_sampler = VK_NULL_HANDLE;
|
||||
VkImage m_texImage = VK_NULL_HANDLE;
|
||||
VkDeviceMemory m_texMem = VK_NULL_HANDLE;
|
||||
bool m_texLayoutPending = false;
|
||||
VkImageView m_texView = VK_NULL_HANDLE;
|
||||
VkImage m_texStaging = VK_NULL_HANDLE;
|
||||
VkDeviceMemory m_texStagingMem = VK_NULL_HANDLE;
|
||||
bool m_texStagingPending = false;
|
||||
QSize m_texSize;
|
||||
VkFormat m_texFormat;
|
||||
|
||||
QMatrix4x4 m_proj;
|
||||
float m_rotation = 0.0f;
|
||||
};
|
||||
|
||||
class VulkanWindow : public QVulkanWindow
|
||||
{
|
||||
public:
|
||||
QVulkanWindowRenderer *createRenderer() override;
|
||||
};
|
||||
|
||||
#endif // HELLOVULKANTEXTURE_H
|
@ -0,0 +1,7 @@
|
||||
HEADERS += hellovulkantexture.h
|
||||
SOURCES += hellovulkantexture.cpp main.cpp
|
||||
RESOURCES += hellovulkantexture.qrc
|
||||
|
||||
# install
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/vulkan/hellovulkantexture
|
||||
INSTALLS += target
|
@ -0,0 +1,7 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
<file>texture_vert.spv</file>
|
||||
<file>texture_frag.spv</file>
|
||||
<file>qt256.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
32
tests/manual/examples/vulkan/hellovulkantexture/main.cpp
Normal file
32
tests/manual/examples/vulkan/hellovulkantexture/main.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2017 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QVulkanInstance>
|
||||
#include <QLoggingCategory>
|
||||
#include "hellovulkantexture.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(lcVk, "qt.vulkan")
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
QLoggingCategory::setFilterRules(QStringLiteral("qt.vulkan=true"));
|
||||
|
||||
QVulkanInstance inst;
|
||||
inst.setLayers({ "VK_LAYER_KHRONOS_validation" });
|
||||
|
||||
if (!inst.create())
|
||||
qFatal("Failed to create Vulkan instance: %d", inst.errorCode());
|
||||
|
||||
VulkanWindow w;
|
||||
w.setVulkanInstance(&inst);
|
||||
if (QCoreApplication::arguments().contains(QStringLiteral("--srgb")))
|
||||
w.setPreferredColorFormats(QList<VkFormat>() << VK_FORMAT_B8G8R8A8_SRGB);
|
||||
|
||||
w.resize(1024, 768);
|
||||
w.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
BIN
tests/manual/examples/vulkan/hellovulkantexture/qt256.png
Normal file
BIN
tests/manual/examples/vulkan/hellovulkantexture/qt256.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.1 KiB |
12
tests/manual/examples/vulkan/hellovulkantexture/texture.frag
Normal file
12
tests/manual/examples/vulkan/hellovulkantexture/texture.frag
Normal file
@ -0,0 +1,12 @@
|
||||
#version 440
|
||||
|
||||
layout(location = 0) in vec2 v_texcoord;
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
layout(binding = 1) uniform sampler2D tex;
|
||||
|
||||
void main()
|
||||
{
|
||||
fragColor = texture(tex, v_texcoord);
|
||||
}
|
18
tests/manual/examples/vulkan/hellovulkantexture/texture.vert
Normal file
18
tests/manual/examples/vulkan/hellovulkantexture/texture.vert
Normal file
@ -0,0 +1,18 @@
|
||||
#version 440
|
||||
|
||||
layout(location = 0) in vec4 position;
|
||||
layout(location = 1) in vec2 texcoord;
|
||||
|
||||
layout(location = 0) out vec2 v_texcoord;
|
||||
|
||||
layout(std140, binding = 0) uniform buf {
|
||||
mat4 mvp;
|
||||
} ubuf;
|
||||
|
||||
out gl_PerVertex { vec4 gl_Position; };
|
||||
|
||||
void main()
|
||||
{
|
||||
v_texcoord = texcoord;
|
||||
gl_Position = ubuf.mvp * position;
|
||||
}
|
BIN
tests/manual/examples/vulkan/hellovulkantexture/texture_frag.spv
Normal file
BIN
tests/manual/examples/vulkan/hellovulkantexture/texture_frag.spv
Normal file
Binary file not shown.
BIN
tests/manual/examples/vulkan/hellovulkantexture/texture_vert.spv
Normal file
BIN
tests/manual/examples/vulkan/hellovulkantexture/texture_vert.spv
Normal file
Binary file not shown.
Reference in New Issue
Block a user