Introduce the Qt graphics abstraction as private QtGui helpers

Comes with backends for Vulkan, Metal, Direct3D 11.1, and OpenGL (ES).

All APIs are private for now.

Shader conditioning (i.e. generating a QRhiShader in memory or on disk
from some shader source code) is done via the tools and APIs provided
by qt-labs/qtshadertools.

The OpenGL support follows the cross-platform tradition of requiring
ES 2.0 only, while optionally using some (ES) 3.x features. It can
operate in core profile contexts as well.

Task-number: QTBUG-70287
Change-Id: I246f2e36d562e404012c05db2aa72487108aa7cc
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Laszlo Agocs 2019-03-22 09:55:03 +01:00
parent c143161608
commit 53599592e0
139 changed files with 38489 additions and 1 deletions

View File

@ -49,6 +49,7 @@ qtConfig(animation): include(animation/animation.pri)
include(itemmodels/itemmodels.pri)
include(vulkan/vulkan.pri)
include(platform/platform.pri)
include(rhi/rhi.pri)
QMAKE_LIBS += $$QMAKE_LIBS_GUI

4891
src/gui/rhi/qrhi.cpp Normal file

File diff suppressed because it is too large Load Diff

1375
src/gui/rhi/qrhi_p.h Normal file

File diff suppressed because it is too large Load Diff

544
src/gui/rhi/qrhi_p_p.h Normal file
View File

@ -0,0 +1,544 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QRHI_P_H
#define QRHI_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qrhi_p.h"
#include "qrhiprofiler_p_p.h"
#include <QBitArray>
#include <QAtomicInt>
#include <QAtomicInteger>
QT_BEGIN_NAMESPACE
#define QRHI_RES(t, x) static_cast<t *>(x)
#define QRHI_RES_RHI(t) t *rhiD = static_cast<t *>(m_rhi)
#define QRHI_PROF QRhiProfilerPrivate *rhiP = m_rhi->profilerPrivateOrNull()
#define QRHI_PROF_F(f) for (bool qrhip_enabled = rhiP != nullptr; qrhip_enabled; qrhip_enabled = false) rhiP->f
class QRhiImplementation
{
public:
virtual ~QRhiImplementation();
virtual bool create(QRhi::Flags flags) = 0;
virtual void destroy() = 0;
virtual QRhiGraphicsPipeline *createGraphicsPipeline() = 0;
virtual QRhiShaderResourceBindings *createShaderResourceBindings() = 0;
virtual QRhiBuffer *createBuffer(QRhiBuffer::Type type,
QRhiBuffer::UsageFlags usage,
int size) = 0;
virtual QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
const QSize &pixelSize,
int sampleCount,
QRhiRenderBuffer::Flags flags) = 0;
virtual QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize,
int sampleCount,
QRhiTexture::Flags flags) = 0;
virtual QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
QRhiSampler::Filter mipmapMode,
QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) = 0;
virtual QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
QRhiTextureRenderTarget::Flags flags) = 0;
virtual QRhiSwapChain *createSwapChain() = 0;
virtual QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) = 0;
virtual QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) = 0;
virtual QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) = 0;
virtual QRhi::FrameOpResult endOffscreenFrame() = 0;
virtual QRhi::FrameOpResult finish() = 0;
virtual void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
virtual void beginPass(QRhiCommandBuffer *cb,
QRhiRenderTarget *rt,
const QColor &colorClearValue,
const QRhiDepthStencilClearValue &depthStencilClearValue,
QRhiResourceUpdateBatch *resourceUpdates) = 0;
virtual void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
virtual void setGraphicsPipeline(QRhiCommandBuffer *cb,
QRhiGraphicsPipeline *ps) = 0;
virtual void setShaderResources(QRhiCommandBuffer *cb,
QRhiShaderResourceBindings *srb,
int dynamicOffsetCount,
const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) = 0;
virtual void setVertexInput(QRhiCommandBuffer *cb,
int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
QRhiBuffer *indexBuf, quint32 indexOffset,
QRhiCommandBuffer::IndexFormat indexFormat) = 0;
virtual void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) = 0;
virtual void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) = 0;
virtual void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) = 0;
virtual void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) = 0;
virtual void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) = 0;
virtual void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
quint32 instanceCount, quint32 firstIndex,
qint32 vertexOffset, quint32 firstInstance) = 0;
virtual void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) = 0;
virtual void debugMarkEnd(QRhiCommandBuffer *cb) = 0;
virtual void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) = 0;
virtual const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) = 0;
virtual void beginExternal(QRhiCommandBuffer *cb) = 0;
virtual void endExternal(QRhiCommandBuffer *cb) = 0;
virtual QVector<int> supportedSampleCounts() const = 0;
virtual int ubufAlignment() const = 0;
virtual bool isYUpInFramebuffer() const = 0;
virtual bool isYUpInNDC() const = 0;
virtual bool isClipDepthZeroToOne() const = 0;
virtual QMatrix4x4 clipSpaceCorrMatrix() const = 0;
virtual bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const = 0;
virtual bool isFeatureSupported(QRhi::Feature feature) const = 0;
virtual int resourceLimit(QRhi::ResourceLimit limit) const = 0;
virtual const QRhiNativeHandles *nativeHandles() = 0;
virtual void sendVMemStatsToProfiler() = 0;
bool isCompressedFormat(QRhiTexture::Format format) const;
void compressedFormatInfo(QRhiTexture::Format format, const QSize &size,
quint32 *bpl, quint32 *byteSize,
QSize *blockDim) const;
void textureFormatInfo(QRhiTexture::Format format, const QSize &size,
quint32 *bpl, quint32 *byteSize) const;
quint32 approxByteSizeForTexture(QRhiTexture::Format format, const QSize &baseSize,
int mipCount, int layerCount);
QRhiProfilerPrivate *profilerPrivateOrNull()
{
// return null when QRhi::EnableProfiling was not set
QRhiProfilerPrivate *p = QRhiProfilerPrivate::get(&profiler);
return p->rhiDWhenEnabled ? p : nullptr;
}
// only really care about resources that own native graphics resources underneath
void registerResource(QRhiResource *res)
{
resources.insert(res);
}
void unregisterResource(QRhiResource *res)
{
resources.remove(res);
}
QSet<QRhiResource *> activeResources() const
{
return resources;
}
void addReleaseAndDestroyLater(QRhiResource *res)
{
if (inFrame)
pendingReleaseAndDestroyResources.insert(res);
else
delete res;
}
void addCleanupCallback(const QRhi::CleanupCallback &callback)
{
cleanupCallbacks.append(callback);
}
QRhi *q;
protected:
bool debugMarkers = false;
int currentFrameSlot = 0; // for vk, mtl, and similar. unused by gl and d3d11.
private:
QRhi::Implementation implType;
QThread *implThread;
QRhiProfiler profiler;
QVector<QRhiResourceUpdateBatch *> resUpdPool;
QBitArray resUpdPoolMap;
QSet<QRhiResource *> resources;
QSet<QRhiResource *> pendingReleaseAndDestroyResources;
QVector<QRhi::CleanupCallback> cleanupCallbacks;
bool inFrame = false;
friend class QRhi;
friend class QRhiResourceUpdateBatchPrivate;
};
template<typename T, size_t N>
bool qrhi_toTopLeftRenderTargetRect(const QSize &outputSize, const std::array<T, N> &r,
T *x, T *y, T *w, T *h)
{
// x,y are bottom-left in QRhiScissor and QRhiViewport but top-left in
// Vulkan/Metal/D3D. We also need proper clamping since some
// validation/debug layers are allergic to out of bounds scissor or
// viewport rects.
const T outputWidth = outputSize.width();
const T outputHeight = outputSize.height();
const T inputWidth = r[2];
const T inputHeight = r[3];
*x = qMax<T>(0, r[0]);
*y = qMax<T>(0, outputHeight - (r[1] + inputHeight));
*w = inputWidth;
*h = inputHeight;
if (*x >= outputWidth || *y >= outputHeight)
return false;
if (*x + *w > outputWidth)
*w = outputWidth - *x;
if (*y + *h > outputHeight)
*h = outputHeight - *y;
return true;
}
class QRhiResourceUpdateBatchPrivate
{
public:
struct DynamicBufferUpdate {
DynamicBufferUpdate() { }
DynamicBufferUpdate(QRhiBuffer *buf_, int offset_, int size_, const void *data_)
: buf(buf_), offset(offset_), data(reinterpret_cast<const char *>(data_), size_)
{ }
QRhiBuffer *buf = nullptr;
int offset = 0;
QByteArray data;
};
struct StaticBufferUpload {
StaticBufferUpload() { }
StaticBufferUpload(QRhiBuffer *buf_, int offset_, int size_, const void *data_)
: buf(buf_), offset(offset_), data(reinterpret_cast<const char *>(data_), size_ ? size_ : buf_->size())
{ }
QRhiBuffer *buf = nullptr;
int offset = 0;
QByteArray data;
};
struct TextureOp {
enum Type {
Upload,
Copy,
Read,
MipGen
};
Type type;
struct SUpload {
QRhiTexture *tex = nullptr;
// Specifying multiple uploads for a subresource must be supported.
// In the backend this can then end up, where applicable, as a
// single, batched copy operation with only one set of barriers.
// This helps when doing for example glyph cache fills.
QVector<QRhiTextureSubresourceUploadDescription> subresDesc[QRhi::MAX_LAYERS][QRhi::MAX_LEVELS];
} upload;
struct SCopy {
QRhiTexture *dst = nullptr;
QRhiTexture *src = nullptr;
QRhiTextureCopyDescription desc;
} copy;
struct SRead {
QRhiReadbackDescription rb;
QRhiReadbackResult *result;
} read;
struct SMipGen {
QRhiTexture *tex = nullptr;
int layer = 0;
} mipgen;
static TextureOp textureUpload(QRhiTexture *tex, const QRhiTextureUploadDescription &desc)
{
TextureOp op;
op.type = Upload;
op.upload.tex = tex;
const QVector<QRhiTextureUploadEntry> &entries(desc.entries());
for (const QRhiTextureUploadEntry &entry : entries)
op.upload.subresDesc[entry.layer()][entry.level()].append(entry.description());
return op;
}
static TextureOp textureCopy(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc)
{
TextureOp op;
op.type = Copy;
op.copy.dst = dst;
op.copy.src = src;
op.copy.desc = desc;
return op;
}
static TextureOp textureRead(const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
{
TextureOp op;
op.type = Read;
op.read.rb = rb;
op.read.result = result;
return op;
}
static TextureOp textureMipGen(QRhiTexture *tex, int layer)
{
TextureOp op;
op.type = MipGen;
op.mipgen.tex = tex;
op.mipgen.layer = layer;
return op;
}
};
QVector<DynamicBufferUpdate> dynamicBufferUpdates;
QVector<StaticBufferUpload> staticBufferUploads;
QVector<TextureOp> textureOps;
QRhiResourceUpdateBatch *q = nullptr;
QRhiImplementation *rhi = nullptr;
int poolIndex = -1;
void free();
void merge(QRhiResourceUpdateBatchPrivate *other);
static QRhiResourceUpdateBatchPrivate *get(QRhiResourceUpdateBatch *b) { return b->d; }
};
Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::StaticBufferUpload, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::TextureOp, Q_MOVABLE_TYPE);
class Q_GUI_EXPORT QRhiShaderResourceBindingPrivate
{
public:
QRhiShaderResourceBindingPrivate()
: ref(1)
{
}
QRhiShaderResourceBindingPrivate(const QRhiShaderResourceBindingPrivate *other)
: ref(1),
binding(other->binding),
stage(other->stage),
type(other->type),
u(other->u)
{
}
static QRhiShaderResourceBindingPrivate *get(QRhiShaderResourceBinding *s) { return s->d; }
static const QRhiShaderResourceBindingPrivate *get(const QRhiShaderResourceBinding *s) { return s->d; }
QAtomicInt ref;
int binding;
QRhiShaderResourceBinding::StageFlags stage;
QRhiShaderResourceBinding::Type type;
struct UniformBufferData {
QRhiBuffer *buf;
int offset;
int maybeSize;
bool hasDynamicOffset;
};
struct SampledTextureData {
QRhiTexture *tex;
QRhiSampler *sampler;
};
union {
UniformBufferData ubuf;
SampledTextureData stex;
} u;
};
template<typename T>
struct QRhiBatchedBindings
{
void feed(int binding, T resource) { // binding must be strictly increasing
if (curBinding == -1 || binding > curBinding + 1) {
finish();
curBatch.startBinding = binding;
curBatch.resources.clear();
curBatch.resources.append(resource);
} else {
Q_ASSERT(binding == curBinding + 1);
curBatch.resources.append(resource);
}
curBinding = binding;
}
void finish() {
if (!curBatch.resources.isEmpty())
batches.append(curBatch);
}
void clear() {
batches.clear();
curBatch.resources.clear();
curBinding = -1;
}
struct Batch {
uint startBinding;
QVarLengthArray<T, 4> resources;
bool operator==(const Batch &other) const
{
return startBinding == other.startBinding && resources == other.resources;
}
bool operator!=(const Batch &other) const
{
return !operator==(other);
}
};
QVarLengthArray<Batch, 4> batches; // sorted by startBinding
bool operator==(const QRhiBatchedBindings<T> &other) const
{
return batches == other.batches;
}
bool operator!=(const QRhiBatchedBindings<T> &other) const
{
return !operator==(other);
}
private:
Batch curBatch;
int curBinding = -1;
};
class QRhiGlobalObjectIdGenerator
{
public:
#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
using Type = quint64;
#else
using Type = quint32;
#endif
static Type newId();
private:
QAtomicInteger<Type> counter;
};
class QRhiPassResourceTracker
{
public:
bool isEmpty() const;
void reset();
struct UsageState {
int layout;
int access;
int stage;
};
enum BufferStage {
BufVertexInputStage,
BufVertexStage,
BufFragmentStage
};
enum BufferAccess {
BufVertexInput,
BufIndexRead,
BufUniformRead
};
void registerBufferOnce(QRhiBuffer *buf, int slot, BufferAccess access, BufferStage stage,
const UsageState &stateAtPassBegin);
enum TextureStage {
TexVertexStage,
TexFragmentStage,
TexColorOutputStage,
TexDepthOutputStage
};
enum TextureAccess {
TexSample,
TexColorOutput,
TexDepthOutput
};
void registerTextureOnce(QRhiTexture *tex, TextureAccess access, TextureStage stage,
const UsageState &stateAtPassBegin);
struct Buffer {
QRhiBuffer *buf;
int slot;
BufferAccess access;
BufferStage stage;
UsageState stateAtPassBegin;
};
const QVector<Buffer> *buffers() const { return &m_buffers; }
struct Texture {
QRhiTexture *tex;
TextureAccess access;
TextureStage stage;
UsageState stateAtPassBegin;
};
const QVector<Texture> *textures() const { return &m_textures; }
private:
QVector<Buffer> m_buffers;
QVector<Texture> m_textures;
};
Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Buffer, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Texture, Q_MOVABLE_TYPE);
QT_END_NAMESPACE
#endif

3388
src/gui/rhi/qrhid3d11.cpp Normal file

File diff suppressed because it is too large Load Diff

76
src/gui/rhi/qrhid3d11_p.h Normal file
View File

@ -0,0 +1,76 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QRHID3D11_H
#define QRHID3D11_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <private/qrhi_p.h>
// no d3d includes here, to prevent precompiled header mess (due to this being
// a public header)
QT_BEGIN_NAMESPACE
struct Q_GUI_EXPORT QRhiD3D11InitParams : public QRhiInitParams
{
bool enableDebugLayer = false;
};
struct Q_GUI_EXPORT QRhiD3D11NativeHandles : public QRhiNativeHandles
{
void *dev = nullptr;
void *context = nullptr;
};
struct Q_GUI_EXPORT QRhiD3D11TextureNativeHandles : public QRhiNativeHandles
{
void *texture = nullptr; // ID3D11Texture2D*
};
QT_END_NAMESPACE
#endif

624
src/gui/rhi/qrhid3d11_p_p.h Normal file
View File

@ -0,0 +1,624 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QRHID3D11_P_H
#define QRHID3D11_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qrhid3d11_p.h"
#include "qrhi_p_p.h"
#include "qshaderdescription_p.h"
#include <QWindow>
#include <d3d11_1.h>
#include <dxgi1_3.h>
QT_BEGIN_NAMESPACE
struct QD3D11Buffer : public QRhiBuffer
{
QD3D11Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size);
~QD3D11Buffer();
void release() override;
bool build() override;
ID3D11Buffer *buffer = nullptr;
QByteArray dynBuf;
bool hasPendingDynamicUpdates = false;
uint generation = 0;
friend class QRhiD3D11;
};
struct QD3D11RenderBuffer : public QRhiRenderBuffer
{
QD3D11RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags);
~QD3D11RenderBuffer();
void release() override;
bool build() override;
QRhiTexture::Format backingFormat() const override;
ID3D11Texture2D *tex = nullptr;
ID3D11DepthStencilView *dsv = nullptr;
ID3D11RenderTargetView *rtv = nullptr;
DXGI_FORMAT dxgiFormat;
DXGI_SAMPLE_DESC sampleDesc;
friend class QRhiD3D11;
};
struct QD3D11Texture : public QRhiTexture
{
QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
int sampleCount, Flags flags);
~QD3D11Texture();
void release() override;
bool build() override;
bool buildFrom(const QRhiNativeHandles *src) override;
const QRhiNativeHandles *nativeHandles() override;
bool prepareBuild(QSize *adjustedSize = nullptr);
bool finishBuild();
ID3D11Texture2D *tex = nullptr;
bool owns = true;
ID3D11ShaderResourceView *srv = nullptr;
DXGI_FORMAT dxgiFormat;
uint mipLevelCount = 0;
DXGI_SAMPLE_DESC sampleDesc;
QRhiD3D11TextureNativeHandles nativeHandlesStruct;
uint generation = 0;
friend class QRhiD3D11;
};
struct QD3D11Sampler : public QRhiSampler
{
QD3D11Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
AddressMode u, AddressMode v);
~QD3D11Sampler();
void release() override;
bool build() override;
ID3D11SamplerState *samplerState = nullptr;
uint generation = 0;
friend class QRhiD3D11;
};
struct QD3D11RenderPassDescriptor : public QRhiRenderPassDescriptor
{
QD3D11RenderPassDescriptor(QRhiImplementation *rhi);
~QD3D11RenderPassDescriptor();
void release() override;
};
struct QD3D11RenderTargetData
{
QD3D11RenderTargetData(QRhiImplementation *)
{
for (int i = 0; i < MAX_COLOR_ATTACHMENTS; ++i)
rtv[i] = nullptr;
}
QD3D11RenderPassDescriptor *rp = nullptr;
QSize pixelSize;
float dpr = 1;
int sampleCount = 1;
int colorAttCount = 0;
int dsAttCount = 0;
static const int MAX_COLOR_ATTACHMENTS = 8;
ID3D11RenderTargetView *rtv[MAX_COLOR_ATTACHMENTS];
ID3D11DepthStencilView *dsv = nullptr;
};
struct QD3D11ReferenceRenderTarget : public QRhiRenderTarget
{
QD3D11ReferenceRenderTarget(QRhiImplementation *rhi);
~QD3D11ReferenceRenderTarget();
void release() override;
QSize pixelSize() const override;
float devicePixelRatio() const override;
int sampleCount() const override;
QD3D11RenderTargetData d;
};
struct QD3D11TextureRenderTarget : public QRhiTextureRenderTarget
{
QD3D11TextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags);
~QD3D11TextureRenderTarget();
void release() override;
QSize pixelSize() const override;
float devicePixelRatio() const override;
int sampleCount() const override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
bool build() override;
QD3D11RenderTargetData d;
bool ownsRtv[QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS];
ID3D11RenderTargetView *rtv[QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS];
bool ownsDsv = false;
ID3D11DepthStencilView *dsv = nullptr;
friend class QRhiD3D11;
};
struct QD3D11ShaderResourceBindings : public QRhiShaderResourceBindings
{
QD3D11ShaderResourceBindings(QRhiImplementation *rhi);
~QD3D11ShaderResourceBindings();
void release() override;
bool build() override;
QVector<QRhiShaderResourceBinding> sortedBindings;
uint generation = 0;
// Keep track of the generation number of each referenced QRhi* to be able
// to detect that the batched bindings are out of date.
struct BoundUniformBufferData {
quint64 id;
uint generation;
};
struct BoundSampledTextureData {
quint64 texId;
uint texGeneration;
quint64 samplerId;
uint samplerGeneration;
};
struct BoundResourceData {
union {
BoundUniformBufferData ubuf;
BoundSampledTextureData stex;
};
};
QVector<BoundResourceData> boundResourceData;
QRhiBatchedBindings<ID3D11Buffer *> vsubufs;
QRhiBatchedBindings<UINT> vsubufoffsets;
QRhiBatchedBindings<UINT> vsubufsizes;
QRhiBatchedBindings<ID3D11Buffer *> fsubufs;
QRhiBatchedBindings<UINT> fsubufoffsets;
QRhiBatchedBindings<UINT> fsubufsizes;
QRhiBatchedBindings<ID3D11SamplerState *> vssamplers;
QRhiBatchedBindings<ID3D11ShaderResourceView *> vsshaderresources;
QRhiBatchedBindings<ID3D11SamplerState *> fssamplers;
QRhiBatchedBindings<ID3D11ShaderResourceView *> fsshaderresources;
friend class QRhiD3D11;
};
Q_DECLARE_TYPEINFO(QD3D11ShaderResourceBindings::BoundResourceData, Q_MOVABLE_TYPE);
struct QD3D11GraphicsPipeline : public QRhiGraphicsPipeline
{
QD3D11GraphicsPipeline(QRhiImplementation *rhi);
~QD3D11GraphicsPipeline();
void release() override;
bool build() override;
ID3D11DepthStencilState *dsState = nullptr;
ID3D11BlendState *blendState = nullptr;
ID3D11VertexShader *vs = nullptr;
ID3D11PixelShader *fs = nullptr;
ID3D11InputLayout *inputLayout = nullptr;
D3D11_PRIMITIVE_TOPOLOGY d3dTopology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
ID3D11RasterizerState *rastState = nullptr;
uint generation = 0;
friend class QRhiD3D11;
};
struct QD3D11SwapChain;
struct QD3D11CommandBuffer : public QRhiCommandBuffer
{
QD3D11CommandBuffer(QRhiImplementation *rhi);
~QD3D11CommandBuffer();
void release() override;
struct Command {
enum Cmd {
SetRenderTarget,
Clear,
Viewport,
Scissor,
BindVertexBuffers,
BindIndexBuffer,
BindGraphicsPipeline,
BindShaderResources,
StencilRef,
BlendConstants,
Draw,
DrawIndexed,
UpdateSubRes,
CopySubRes,
ResolveSubRes,
GenMip,
DebugMarkBegin,
DebugMarkEnd,
DebugMarkMsg
};
enum ClearFlag { Color = 1, Depth = 2, Stencil = 4 };
Cmd cmd;
static const int MAX_UBUF_BINDINGS = 32; // should be D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT but 128 is a waste of space for our purposes
// QRhi*/QD3D11* references should be kept at minimum (so no
// QRhiTexture/Buffer/etc. pointers).
union {
struct {
QRhiRenderTarget *rt;
} setRenderTarget;
struct {
QRhiRenderTarget *rt;
int mask;
float c[4];
float d;
quint32 s;
} clear;
struct {
float x, y, w, h;
float d0, d1;
} viewport;
struct {
int x, y, w, h;
} scissor;
struct {
int startSlot;
int slotCount;
ID3D11Buffer *buffers[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
UINT offsets[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
UINT strides[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
} bindVertexBuffers;
struct {
ID3D11Buffer *buffer;
quint32 offset;
DXGI_FORMAT format;
} bindIndexBuffer;
struct {
QD3D11GraphicsPipeline *ps;
} bindGraphicsPipeline;
struct {
QD3D11ShaderResourceBindings *srb;
bool offsetOnlyChange;
int dynamicOffsetCount;
uint dynamicOffsetPairs[MAX_UBUF_BINDINGS * 2]; // binding, offsetInConstants
} bindShaderResources;
struct {
QD3D11GraphicsPipeline *ps;
quint32 ref;
} stencilRef;
struct {
QD3D11GraphicsPipeline *ps;
float c[4];
} blendConstants;
struct {
QD3D11GraphicsPipeline *ps;
quint32 vertexCount;
quint32 instanceCount;
quint32 firstVertex;
quint32 firstInstance;
} draw;
struct {
QD3D11GraphicsPipeline *ps;
quint32 indexCount;
quint32 instanceCount;
quint32 firstIndex;
qint32 vertexOffset;
quint32 firstInstance;
} drawIndexed;
struct {
ID3D11Resource *dst;
UINT dstSubRes;
bool hasDstBox;
D3D11_BOX dstBox;
const void *src; // must come from retain*()
UINT srcRowPitch;
} updateSubRes;
struct {
ID3D11Resource *dst;
UINT dstSubRes;
UINT dstX;
UINT dstY;
ID3D11Resource *src;
UINT srcSubRes;
bool hasSrcBox;
D3D11_BOX srcBox;
} copySubRes;
struct {
ID3D11Resource *dst;
UINT dstSubRes;
ID3D11Resource *src;
UINT srcSubRes;
DXGI_FORMAT format;
} resolveSubRes;
struct {
ID3D11ShaderResourceView *srv;
} genMip;
struct {
char s[64];
} debugMark;
} args;
};
QVector<Command> commands;
QRhiRenderTarget *currentTarget;
QRhiGraphicsPipeline *currentPipeline;
uint currentPipelineGeneration;
QRhiShaderResourceBindings *currentSrb;
uint currentSrbGeneration;
ID3D11Buffer *currentIndexBuffer;
quint32 currentIndexOffset;
DXGI_FORMAT currentIndexFormat;
ID3D11Buffer *currentVertexBuffers[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
quint32 currentVertexOffsets[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
QVector<QByteArray> dataRetainPool;
QVector<QImage> imageRetainPool;
// relies heavily on implicit sharing (no copies of the actual data will be made)
const uchar *retainData(const QByteArray &data) {
dataRetainPool.append(data);
return reinterpret_cast<const uchar *>(dataRetainPool.constLast().constData());
}
const uchar *retainImage(const QImage &image) {
imageRetainPool.append(image);
return imageRetainPool.constLast().constBits();
}
void resetCommands() {
commands.clear();
dataRetainPool.clear();
imageRetainPool.clear();
}
void resetState() {
resetCommands();
currentTarget = nullptr;
resetCachedState();
}
void resetCachedState() {
currentPipeline = nullptr;
currentPipelineGeneration = 0;
currentSrb = nullptr;
currentSrbGeneration = 0;
currentIndexBuffer = nullptr;
currentIndexOffset = 0;
currentIndexFormat = DXGI_FORMAT_R16_UINT;
memset(currentVertexBuffers, 0, sizeof(currentVertexBuffers));
memset(currentVertexOffsets, 0, sizeof(currentVertexOffsets));
}
};
Q_DECLARE_TYPEINFO(QD3D11CommandBuffer::Command, Q_MOVABLE_TYPE);
struct QD3D11SwapChain : public QRhiSwapChain
{
QD3D11SwapChain(QRhiImplementation *rhi);
~QD3D11SwapChain();
void release() override;
QRhiCommandBuffer *currentFrameCommandBuffer() override;
QRhiRenderTarget *currentFrameRenderTarget() override;
QSize surfacePixelSize() override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
bool buildOrResize() override;
void releaseBuffers();
bool newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI_SAMPLE_DESC sampleDesc,
ID3D11Texture2D **tex, ID3D11RenderTargetView **rtv) const;
QWindow *window = nullptr;
QSize pixelSize;
QD3D11ReferenceRenderTarget rt;
QD3D11CommandBuffer cb;
DXGI_FORMAT colorFormat;
IDXGISwapChain *swapChain = nullptr;
static const int BUFFER_COUNT = 2;
ID3D11Texture2D *tex[BUFFER_COUNT];
ID3D11RenderTargetView *rtv[BUFFER_COUNT];
ID3D11Texture2D *msaaTex[BUFFER_COUNT];
ID3D11RenderTargetView *msaaRtv[BUFFER_COUNT];
DXGI_SAMPLE_DESC sampleDesc;
int currentFrameSlot = 0;
int frameCount = 0;
QD3D11RenderBuffer *ds = nullptr;
bool timestampActive[BUFFER_COUNT];
ID3D11Query *timestampDisjointQuery[BUFFER_COUNT];
ID3D11Query *timestampQuery[BUFFER_COUNT * 2];
UINT swapInterval = 1;
};
class QRhiD3D11 : public QRhiImplementation
{
public:
QRhiD3D11(QRhiD3D11InitParams *params, QRhiD3D11NativeHandles *importDevice = nullptr);
bool create(QRhi::Flags flags) override;
void destroy() override;
QRhiGraphicsPipeline *createGraphicsPipeline() override;
QRhiShaderResourceBindings *createShaderResourceBindings() override;
QRhiBuffer *createBuffer(QRhiBuffer::Type type,
QRhiBuffer::UsageFlags usage,
int size) override;
QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
const QSize &pixelSize,
int sampleCount,
QRhiRenderBuffer::Flags flags) override;
QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize,
int sampleCount,
QRhiTexture::Flags flags) override;
QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
QRhiSampler::Filter mipmapMode,
QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override;
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
QRhiTextureRenderTarget::Flags flags) override;
QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
QRhi::FrameOpResult endOffscreenFrame() override;
QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
void beginPass(QRhiCommandBuffer *cb,
QRhiRenderTarget *rt,
const QColor &colorClearValue,
const QRhiDepthStencilClearValue &depthStencilClearValue,
QRhiResourceUpdateBatch *resourceUpdates) override;
void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
void setGraphicsPipeline(QRhiCommandBuffer *cb,
QRhiGraphicsPipeline *ps) override;
void setShaderResources(QRhiCommandBuffer *cb,
QRhiShaderResourceBindings *srb,
int dynamicOffsetCount,
const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override;
void setVertexInput(QRhiCommandBuffer *cb,
int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
QRhiBuffer *indexBuf, quint32 indexOffset,
QRhiCommandBuffer::IndexFormat indexFormat) override;
void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override;
void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
quint32 instanceCount, quint32 firstIndex,
qint32 vertexOffset, quint32 firstInstance) override;
void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override;
void debugMarkEnd(QRhiCommandBuffer *cb) override;
void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override;
const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override;
void beginExternal(QRhiCommandBuffer *cb) override;
void endExternal(QRhiCommandBuffer *cb) override;
QVector<int> supportedSampleCounts() const override;
int ubufAlignment() const override;
bool isYUpInFramebuffer() const override;
bool isYUpInNDC() const override;
bool isClipDepthZeroToOne() const override;
QMatrix4x4 clipSpaceCorrMatrix() const override;
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override;
bool isFeatureSupported(QRhi::Feature feature) const override;
int resourceLimit(QRhi::ResourceLimit limit) const override;
const QRhiNativeHandles *nativeHandles() override;
void sendVMemStatsToProfiler() override;
void flushCommandBuffer();
void enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc);
void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
void updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD);
void executeBufferHostWritesForCurrentFrame(QD3D11Buffer *bufD);
void bindShaderResources(QD3D11ShaderResourceBindings *srbD,
const uint *dynOfsPairs, int dynOfsPairCount,
bool offsetOnlyChange);
void setRenderTarget(QRhiRenderTarget *rt);
void executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *timestampSwapChain = nullptr);
DXGI_SAMPLE_DESC effectiveSampleCount(int sampleCount) const;
void finishActiveReadbacks();
void enqueueSetRenderTarget(QD3D11CommandBuffer *cbD, QRhiRenderTarget *rt);
void reportLiveObjects(ID3D11Device *device);
bool debugLayer = false;
bool importedDevice = false;
ID3D11Device *dev = nullptr;
ID3D11DeviceContext1 *context = nullptr;
D3D_FEATURE_LEVEL featureLevel;
ID3DUserDefinedAnnotation *annotations = nullptr;
IDXGIFactory1 *dxgiFactory = nullptr;
bool hasDxgi2 = false;
QRhiD3D11NativeHandles nativeHandlesStruct;
bool inFrame = false;
bool inPass = false;
struct {
int vsHighestActiveSrvBinding = -1;
int fsHighestActiveSrvBinding = -1;
QD3D11SwapChain *currentSwapChain = nullptr;
} contextState;
struct OffscreenFrame {
OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { }
bool active = false;
QD3D11CommandBuffer cbWrapper;
} ofr;
struct ActiveReadback {
QRhiReadbackDescription desc;
QRhiReadbackResult *result;
ID3D11Texture2D *stagingTex;
quint32 bufSize;
quint32 bpl;
QSize pixelSize;
QRhiTexture::Format format;
};
QVector<ActiveReadback> activeReadbacks;
};
Q_DECLARE_TYPEINFO(QRhiD3D11::ActiveReadback, Q_MOVABLE_TYPE);
QT_END_NAMESPACE
#endif

2975
src/gui/rhi/qrhigles2.cpp Normal file

File diff suppressed because it is too large Load Diff

84
src/gui/rhi/qrhigles2_p.h Normal file
View File

@ -0,0 +1,84 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QRHIGLES2_H
#define QRHIGLES2_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <private/qrhi_p.h>
#include <QtGui/qsurfaceformat.h>
QT_BEGIN_NAMESPACE
class QOpenGLContext;
class QOffscreenSurface;
class QWindow;
struct Q_GUI_EXPORT QRhiGles2InitParams : public QRhiInitParams
{
QRhiGles2InitParams();
QSurfaceFormat format;
QOffscreenSurface *fallbackSurface = nullptr;
QWindow *window = nullptr;
static QOffscreenSurface *newFallbackSurface(const QSurfaceFormat &format = QSurfaceFormat::defaultFormat());
static QSurfaceFormat adjustedFormat(const QSurfaceFormat &format = QSurfaceFormat::defaultFormat());
};
struct Q_GUI_EXPORT QRhiGles2NativeHandles : public QRhiNativeHandles
{
QOpenGLContext *context = nullptr;
};
struct Q_GUI_EXPORT QRhiGles2TextureNativeHandles : public QRhiNativeHandles
{
uint texture = 0;
};
QT_END_NAMESPACE
#endif

695
src/gui/rhi/qrhigles2_p_p.h Normal file
View File

@ -0,0 +1,695 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QRHIGLES2_P_H
#define QRHIGLES2_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qrhigles2_p.h"
#include "qrhi_p_p.h"
#include "qshaderdescription_p.h"
#include <qopengl.h>
#include <QSurface>
QT_BEGIN_NAMESPACE
class QOpenGLExtensions;
struct QGles2Buffer : public QRhiBuffer
{
QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size);
~QGles2Buffer();
void release() override;
bool build() override;
GLuint buffer = 0;
GLenum target;
QByteArray ubuf;
friend class QRhiGles2;
};
struct QGles2RenderBuffer : public QRhiRenderBuffer
{
QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags);
~QGles2RenderBuffer();
void release() override;
bool build() override;
QRhiTexture::Format backingFormat() const override;
GLuint renderbuffer = 0;
GLuint stencilRenderbuffer = 0; // when packed depth-stencil not supported
int samples;
friend class QRhiGles2;
};
struct QGles2SamplerData
{
GLenum glminfilter = 0;
GLenum glmagfilter = 0;
GLenum glwraps = 0;
GLenum glwrapt = 0;
GLenum glwrapr = 0;
GLenum gltexcomparefunc = 0;
};
inline bool operator==(const QGles2SamplerData &a, const QGles2SamplerData &b)
{
return a.glminfilter == b.glminfilter
&& a.glmagfilter == b.glmagfilter
&& a.glwraps == b.glwraps
&& a.glwrapt == b.glwrapt
&& a.glwrapr == b.glwrapr
&& a.gltexcomparefunc == b.gltexcomparefunc;
}
inline bool operator!=(const QGles2SamplerData &a, const QGles2SamplerData &b)
{
return !(a == b);
}
struct QGles2Texture : public QRhiTexture
{
QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
int sampleCount, Flags flags);
~QGles2Texture();
void release() override;
bool build() override;
bool buildFrom(const QRhiNativeHandles *src) override;
const QRhiNativeHandles *nativeHandles() override;
bool prepareBuild(QSize *adjustedSize = nullptr);
GLuint texture = 0;
bool owns = true;
GLenum target;
GLenum glintformat;
GLenum glformat;
GLenum gltype;
QGles2SamplerData samplerState;
bool specified = false;
int mipLevelCount = 0;
QRhiGles2TextureNativeHandles nativeHandlesStruct;
uint generation = 0;
friend class QRhiGles2;
};
struct QGles2Sampler : public QRhiSampler
{
QGles2Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
AddressMode u, AddressMode v);
~QGles2Sampler();
void release() override;
bool build() override;
QGles2SamplerData d;
uint generation = 0;
friend class QRhiGles2;
};
struct QGles2RenderPassDescriptor : public QRhiRenderPassDescriptor
{
QGles2RenderPassDescriptor(QRhiImplementation *rhi);
~QGles2RenderPassDescriptor();
void release() override;
};
struct QGles2RenderTargetData
{
QGles2RenderTargetData(QRhiImplementation *) { }
QGles2RenderPassDescriptor *rp = nullptr;
QSize pixelSize;
float dpr = 1;
int sampleCount = 1;
int colorAttCount = 0;
int dsAttCount = 0;
bool srgbUpdateAndBlend = false;
};
struct QGles2ReferenceRenderTarget : public QRhiRenderTarget
{
QGles2ReferenceRenderTarget(QRhiImplementation *rhi);
~QGles2ReferenceRenderTarget();
void release() override;
QSize pixelSize() const override;
float devicePixelRatio() const override;
int sampleCount() const override;
QGles2RenderTargetData d;
};
struct QGles2TextureRenderTarget : public QRhiTextureRenderTarget
{
QGles2TextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags);
~QGles2TextureRenderTarget();
void release() override;
QSize pixelSize() const override;
float devicePixelRatio() const override;
int sampleCount() const override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
bool build() override;
QGles2RenderTargetData d;
GLuint framebuffer = 0;
friend class QRhiGles2;
};
struct QGles2ShaderResourceBindings : public QRhiShaderResourceBindings
{
QGles2ShaderResourceBindings(QRhiImplementation *rhi);
~QGles2ShaderResourceBindings();
void release() override;
bool build() override;
uint generation = 0;
friend class QRhiGles2;
};
struct QGles2GraphicsPipeline : public QRhiGraphicsPipeline
{
QGles2GraphicsPipeline(QRhiImplementation *rhi);
~QGles2GraphicsPipeline();
void release() override;
bool build() override;
GLuint program = 0;
GLenum drawMode = GL_TRIANGLES;
QShaderDescription vsDesc;
QShaderDescription fsDesc;
bool canUseUniformBuffers = false;
struct Uniform {
QShaderDescription::VariableType type;
int glslLocation;
int binding;
uint offset;
int size;
};
QVector<Uniform> uniforms;
struct Sampler {
int glslLocation;
int binding;
};
QVector<Sampler> samplers;
uint generation = 0;
friend class QRhiGles2;
};
Q_DECLARE_TYPEINFO(QGles2GraphicsPipeline::Uniform, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QGles2GraphicsPipeline::Sampler, Q_MOVABLE_TYPE);
struct QGles2CommandBuffer : public QRhiCommandBuffer
{
QGles2CommandBuffer(QRhiImplementation *rhi);
~QGles2CommandBuffer();
void release() override;
struct Command {
enum Cmd {
BeginFrame,
EndFrame,
Viewport,
Scissor,
BlendConstants,
StencilRef,
BindVertexBuffer,
BindIndexBuffer,
Draw,
DrawIndexed,
BindGraphicsPipeline,
BindShaderResources,
BindFramebuffer,
Clear,
BufferData,
BufferSubData,
CopyTex,
ReadPixels,
SubImage,
CompressedImage,
CompressedSubImage,
BlitFromRenderbuffer,
GenMip
};
Cmd cmd;
static const int MAX_UBUF_BINDINGS = 32; // should be more than enough
// QRhi*/QGles2* references should be kept at minimum (so no
// QRhiTexture/Buffer/etc. pointers).
union {
struct {
float x, y, w, h;
float d0, d1;
} viewport;
struct {
int x, y, w, h;
} scissor;
struct {
float r, g, b, a;
} blendConstants;
struct {
quint32 ref;
QRhiGraphicsPipeline *ps;
} stencilRef;
struct {
QRhiGraphicsPipeline *ps;
GLuint buffer;
quint32 offset;
int binding;
} bindVertexBuffer;
struct {
GLuint buffer;
quint32 offset;
GLenum type;
} bindIndexBuffer;
struct {
QRhiGraphicsPipeline *ps;
quint32 vertexCount;
quint32 firstVertex;
} draw;
struct {
QRhiGraphicsPipeline *ps;
quint32 indexCount;
quint32 firstIndex;
} drawIndexed;
struct {
QRhiGraphicsPipeline *ps;
} bindGraphicsPipeline;
struct {
QRhiGraphicsPipeline *ps;
QRhiShaderResourceBindings *srb;
int dynamicOffsetCount;
uint dynamicOffsetPairs[MAX_UBUF_BINDINGS * 2]; // binding, offsetInConstants
} bindShaderResources;
struct {
GLbitfield mask;
float c[4];
float d;
quint32 s;
} clear;
struct {
GLuint fbo;
bool srgb;
int colorAttCount;
} bindFramebuffer;
struct {
GLenum target;
GLuint buffer;
int offset;
int size;
const void *data; // must come from retainData()
} bufferSubData;
struct {
GLenum srcFaceTarget;
GLuint srcTexture;
int srcLevel;
int srcX;
int srcY;
GLenum dstTarget;
GLuint dstTexture;
GLenum dstFaceTarget;
int dstLevel;
int dstX;
int dstY;
int w;
int h;
} copyTex;
struct {
QRhiReadbackResult *result;
GLuint texture;
int w;
int h;
QRhiTexture::Format format;
GLenum readTarget;
int level;
} readPixels;
struct {
GLenum target;
GLuint texture;
GLenum faceTarget;
int level;
int dx;
int dy;
int w;
int h;
GLenum glformat;
GLenum gltype;
int rowStartAlign;
const void *data; // must come from retainImage()
} subImage;
struct {
GLenum target;
GLuint texture;
GLenum faceTarget;
int level;
GLenum glintformat;
int w;
int h;
int size;
const void *data; // must come from retainData()
} compressedImage;
struct {
GLenum target;
GLuint texture;
GLenum faceTarget;
int level;
int dx;
int dy;
int w;
int h;
GLenum glintformat;
int size;
const void *data; // must come from retainData()
} compressedSubImage;
struct {
GLuint renderbuffer;
int w;
int h;
GLenum target;
GLuint texture;
int dstLevel;
} blitFromRb;
struct {
GLenum target;
GLuint texture;
} genMip;
} args;
};
QVector<Command> commands;
QRhiRenderTarget *currentTarget;
QRhiGraphicsPipeline *currentPipeline;
uint currentPipelineGeneration;
QRhiShaderResourceBindings *currentSrb;
uint currentSrbGeneration;
QVector<QByteArray> dataRetainPool;
QVector<QImage> imageRetainPool;
// relies heavily on implicit sharing (no copies of the actual data will be made)
const void *retainData(const QByteArray &data) {
dataRetainPool.append(data);
return dataRetainPool.constLast().constData();
}
const void *retainImage(const QImage &image) {
imageRetainPool.append(image);
return imageRetainPool.constLast().constBits();
}
void resetCommands() {
commands.clear();
dataRetainPool.clear();
imageRetainPool.clear();
}
void resetState() {
resetCommands();
currentTarget = nullptr;
resetCachedState();
}
void resetCachedState() {
currentPipeline = nullptr;
currentPipelineGeneration = 0;
currentSrb = nullptr;
currentSrbGeneration = 0;
}
};
Q_DECLARE_TYPEINFO(QGles2CommandBuffer::Command, Q_MOVABLE_TYPE);
struct QGles2SwapChain : public QRhiSwapChain
{
QGles2SwapChain(QRhiImplementation *rhi);
~QGles2SwapChain();
void release() override;
QRhiCommandBuffer *currentFrameCommandBuffer() override;
QRhiRenderTarget *currentFrameRenderTarget() override;
QSize surfacePixelSize() override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
bool buildOrResize() override;
QSurface *surface = nullptr;
QSize pixelSize;
QGles2ReferenceRenderTarget rt;
QGles2CommandBuffer cb;
int frameCount = 0;
};
class QRhiGles2 : public QRhiImplementation
{
public:
QRhiGles2(QRhiGles2InitParams *params, QRhiGles2NativeHandles *importDevice = nullptr);
bool create(QRhi::Flags flags) override;
void destroy() override;
QRhiGraphicsPipeline *createGraphicsPipeline() override;
QRhiShaderResourceBindings *createShaderResourceBindings() override;
QRhiBuffer *createBuffer(QRhiBuffer::Type type,
QRhiBuffer::UsageFlags usage,
int size) override;
QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
const QSize &pixelSize,
int sampleCount,
QRhiRenderBuffer::Flags flags) override;
QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize,
int sampleCount,
QRhiTexture::Flags flags) override;
QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
QRhiSampler::Filter mipmapMode,
QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override;
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
QRhiTextureRenderTarget::Flags flags) override;
QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
QRhi::FrameOpResult endOffscreenFrame() override;
QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
void beginPass(QRhiCommandBuffer *cb,
QRhiRenderTarget *rt,
const QColor &colorClearValue,
const QRhiDepthStencilClearValue &depthStencilClearValue,
QRhiResourceUpdateBatch *resourceUpdates) override;
void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
void setGraphicsPipeline(QRhiCommandBuffer *cb,
QRhiGraphicsPipeline *ps) override;
void setShaderResources(QRhiCommandBuffer *cb,
QRhiShaderResourceBindings *srb,
int dynamicOffsetCount,
const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override;
void setVertexInput(QRhiCommandBuffer *cb,
int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
QRhiBuffer *indexBuf, quint32 indexOffset,
QRhiCommandBuffer::IndexFormat indexFormat) override;
void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override;
void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
quint32 instanceCount, quint32 firstIndex,
qint32 vertexOffset, quint32 firstInstance) override;
void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override;
void debugMarkEnd(QRhiCommandBuffer *cb) override;
void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override;
const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override;
void beginExternal(QRhiCommandBuffer *cb) override;
void endExternal(QRhiCommandBuffer *cb) override;
QVector<int> supportedSampleCounts() const override;
int ubufAlignment() const override;
bool isYUpInFramebuffer() const override;
bool isYUpInNDC() const override;
bool isClipDepthZeroToOne() const override;
QMatrix4x4 clipSpaceCorrMatrix() const override;
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override;
bool isFeatureSupported(QRhi::Feature feature) const override;
int resourceLimit(QRhi::ResourceLimit limit) const override;
const QRhiNativeHandles *nativeHandles() override;
void sendVMemStatsToProfiler() override;
bool ensureContext(QSurface *surface = nullptr) const;
void executeDeferredReleases();
QRhi::FrameOpResult flushCommandBuffer();
void enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD,
int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc);
void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
void executeCommandBuffer(QRhiCommandBuffer *cb);
void executeBindGraphicsPipeline(QRhiGraphicsPipeline *ps);
void bindShaderResources(QRhiGraphicsPipeline *ps, QRhiShaderResourceBindings *srb,
const uint *dynOfsPairs, int dynOfsCount);
QGles2RenderTargetData *enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD,
bool *wantsColorClear = nullptr, bool *wantsDsClear = nullptr);
int effectiveSampleCount(int sampleCount) const;
QOpenGLContext *ctx = nullptr;
bool importedContext = false;
QSurfaceFormat requestedFormat;
QSurface *fallbackSurface = nullptr;
QWindow *maybeWindow = nullptr;
mutable bool needsMakeCurrent = false;
QOpenGLExtensions *f = nullptr;
uint vao = 0;
struct Caps {
Caps()
: ctxMajor(2),
ctxMinor(0),
maxTextureSize(2048),
maxDrawBuffers(4),
msaaRenderBuffer(false),
npotTexture(true),
npotTextureRepeat(true),
gles(false),
fixedIndexPrimitiveRestart(false),
bgraExternalFormat(false),
bgraInternalFormat(false),
r8Format(false),
r16Format(false),
floatFormats(false),
depthTexture(false),
packedDepthStencil(false),
srgbCapableDefaultFramebuffer(false),
coreProfile(false),
uniformBuffers(false),
elementIndexUint(false)
{ }
int ctxMajor;
int ctxMinor;
int maxTextureSize;
int maxDrawBuffers;
int maxSamples;
// Multisample fb and blit are supported (GLES 3.0 or OpenGL 3.x). Not
// the same as multisample textures!
uint msaaRenderBuffer : 1;
uint npotTexture : 1;
uint npotTextureRepeat : 1;
uint gles : 1;
uint fixedIndexPrimitiveRestart : 1;
uint bgraExternalFormat : 1;
uint bgraInternalFormat : 1;
uint r8Format : 1;
uint r16Format : 1;
uint floatFormats : 1;
uint depthTexture : 1;
uint packedDepthStencil : 1;
uint srgbCapableDefaultFramebuffer : 1;
uint coreProfile : 1;
uint uniformBuffers : 1;
uint elementIndexUint : 1;
} caps;
bool inFrame = false;
bool inPass = false;
QGles2SwapChain *currentSwapChain = nullptr;
QVector<GLint> supportedCompressedFormats;
mutable QVector<int> supportedSampleCountList;
QRhiGles2NativeHandles nativeHandlesStruct;
struct DeferredReleaseEntry {
enum Type {
Buffer,
Pipeline,
Texture,
RenderBuffer,
TextureRenderTarget
};
Type type;
union {
struct {
GLuint buffer;
} buffer;
struct {
GLuint program;
} pipeline;
struct {
GLuint texture;
} texture;
struct {
GLuint renderbuffer;
GLuint renderbuffer2;
} renderbuffer;
struct {
GLuint framebuffer;
} textureRenderTarget;
};
};
QVector<DeferredReleaseEntry> releaseQueue;
struct OffscreenFrame {
OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { }
bool active = false;
QGles2CommandBuffer cbWrapper;
} ofr;
};
Q_DECLARE_TYPEINFO(QRhiGles2::DeferredReleaseEntry, Q_MOVABLE_TYPE);
QT_END_NAMESPACE
#endif

3249
src/gui/rhi/qrhimetal.mm Normal file

File diff suppressed because it is too large Load Diff

80
src/gui/rhi/qrhimetal_p.h Normal file
View File

@ -0,0 +1,80 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QRHIMETAL_H
#define QRHIMETAL_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <private/qrhi_p.h>
// no Metal includes here, the user code may be plain C++
QT_BEGIN_NAMESPACE
struct Q_GUI_EXPORT QRhiMetalInitParams : public QRhiInitParams
{
};
struct Q_GUI_EXPORT QRhiMetalNativeHandles : public QRhiNativeHandles
{
void *dev = nullptr; // id<MTLDevice>
void *cmdQueue = nullptr; // id<MTLCommandQueue>
};
struct Q_GUI_EXPORT QRhiMetalTextureNativeHandles : public QRhiNativeHandles
{
void *texture = nullptr; // id<MTLTexture>
};
struct Q_GUI_EXPORT QRhiMetalCommandBufferNativeHandles : public QRhiNativeHandles
{
void *commandBuffer = nullptr; // id<MTLCommandBuffer>
void *encoder = nullptr; // id<MTLRenderCommandEncoder>
};
QT_END_NAMESPACE
#endif

410
src/gui/rhi/qrhimetal_p_p.h Normal file
View File

@ -0,0 +1,410 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QRHIMETAL_P_H
#define QRHIMETAL_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qrhimetal_p.h"
#include "qrhi_p_p.h"
#include <QWindow>
QT_BEGIN_NAMESPACE
static const int QMTL_FRAMES_IN_FLIGHT = 2;
// have to hide the ObjC stuff, this header cannot contain MTL* at all
struct QMetalBufferData;
struct QMetalBuffer : public QRhiBuffer
{
QMetalBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size);
~QMetalBuffer();
void release() override;
bool build() override;
QMetalBufferData *d;
uint generation = 0;
int lastActiveFrameSlot = -1;
friend class QRhiMetal;
friend struct QMetalShaderResourceBindings;
};
struct QMetalRenderBufferData;
struct QMetalRenderBuffer : public QRhiRenderBuffer
{
QMetalRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags);
~QMetalRenderBuffer();
void release() override;
bool build() override;
QRhiTexture::Format backingFormat() const override;
QMetalRenderBufferData *d;
int samples = 1;
uint generation = 0;
int lastActiveFrameSlot = -1;
friend class QRhiMetal;
};
struct QMetalTextureData;
struct QMetalTexture : public QRhiTexture
{
QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
int sampleCount, Flags flags);
~QMetalTexture();
void release() override;
bool build() override;
bool buildFrom(const QRhiNativeHandles *src) override;
const QRhiNativeHandles *nativeHandles() override;
bool prepareBuild(QSize *adjustedSize = nullptr);
QMetalTextureData *d;
QRhiMetalTextureNativeHandles nativeHandlesStruct;
int mipLevelCount = 0;
int samples = 1;
uint generation = 0;
int lastActiveFrameSlot = -1;
friend class QRhiMetal;
friend struct QMetalShaderResourceBindings;
};
struct QMetalSamplerData;
struct QMetalSampler : public QRhiSampler
{
QMetalSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
AddressMode u, AddressMode v);
~QMetalSampler();
void release() override;
bool build() override;
QMetalSamplerData *d;
uint generation = 0;
int lastActiveFrameSlot = -1;
friend class QRhiMetal;
friend struct QMetalShaderResourceBindings;
};
struct QMetalRenderPassDescriptor : public QRhiRenderPassDescriptor
{
QMetalRenderPassDescriptor(QRhiImplementation *rhi);
~QMetalRenderPassDescriptor();
void release() override;
// there is no MTLRenderPassDescriptor here as one will be created for each pass in beginPass()
// but the things needed for the render pipeline descriptor have to be provided
static const int MAX_COLOR_ATTACHMENTS = 8;
int colorAttachmentCount = 0;
bool hasDepthStencil = false;
int colorFormat[MAX_COLOR_ATTACHMENTS];
int dsFormat;
};
struct QMetalRenderTargetData;
struct QMetalReferenceRenderTarget : public QRhiRenderTarget
{
QMetalReferenceRenderTarget(QRhiImplementation *rhi);
~QMetalReferenceRenderTarget();
void release() override;
QSize pixelSize() const override;
float devicePixelRatio() const override;
int sampleCount() const override;
QMetalRenderTargetData *d;
};
struct QMetalTextureRenderTarget : public QRhiTextureRenderTarget
{
QMetalTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags);
~QMetalTextureRenderTarget();
void release() override;
QSize pixelSize() const override;
float devicePixelRatio() const override;
int sampleCount() const override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
bool build() override;
QMetalRenderTargetData *d;
friend class QRhiMetal;
};
struct QMetalShaderResourceBindings : public QRhiShaderResourceBindings
{
QMetalShaderResourceBindings(QRhiImplementation *rhi);
~QMetalShaderResourceBindings();
void release() override;
bool build() override;
QVector<QRhiShaderResourceBinding> sortedBindings;
int maxBinding = -1;
struct BoundUniformBufferData {
quint64 id;
uint generation;
};
struct BoundSampledTextureData {
quint64 texId;
uint texGeneration;
quint64 samplerId;
uint samplerGeneration;
};
struct BoundResourceData {
union {
BoundUniformBufferData ubuf;
BoundSampledTextureData stex;
};
};
QVector<BoundResourceData> boundResourceData;
uint generation = 0;
friend class QRhiMetal;
};
struct QMetalGraphicsPipelineData;
struct QMetalGraphicsPipeline : public QRhiGraphicsPipeline
{
QMetalGraphicsPipeline(QRhiImplementation *rhi);
~QMetalGraphicsPipeline();
void release() override;
bool build() override;
QMetalGraphicsPipelineData *d;
uint generation = 0;
int lastActiveFrameSlot = -1;
friend class QRhiMetal;
};
struct QMetalCommandBufferData;
struct QMetalSwapChain;
struct QMetalCommandBuffer : public QRhiCommandBuffer
{
QMetalCommandBuffer(QRhiImplementation *rhi);
~QMetalCommandBuffer();
void release() override;
QMetalCommandBufferData *d = nullptr;
QRhiMetalCommandBufferNativeHandles nativeHandlesStruct;
QRhiRenderTarget *currentTarget;
QRhiGraphicsPipeline *currentPipeline;
uint currentPipelineGeneration;
QRhiShaderResourceBindings *currentSrb;
uint currentSrbGeneration;
int currentResSlot;
QRhiBuffer *currentIndexBuffer;
quint32 currentIndexOffset;
QRhiCommandBuffer::IndexFormat currentIndexFormat;
const QRhiNativeHandles *nativeHandles();
void resetState();
void resetPerPassState();
void resetPerPassCachedState();
};
struct QMetalSwapChainData;
struct QMetalSwapChain : public QRhiSwapChain
{
QMetalSwapChain(QRhiImplementation *rhi);
~QMetalSwapChain();
void release() override;
QRhiCommandBuffer *currentFrameCommandBuffer() override;
QRhiRenderTarget *currentFrameRenderTarget() override;
QSize surfacePixelSize() override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
bool buildOrResize() override;
void chooseFormats();
QWindow *window = nullptr;
QSize pixelSize;
int currentFrameSlot = 0; // 0..QMTL_FRAMES_IN_FLIGHT-1
int frameCount = 0;
int samples = 1;
QMetalReferenceRenderTarget rtWrapper;
QMetalCommandBuffer cbWrapper;
QMetalRenderBuffer *ds = nullptr;
QMetalSwapChainData *d = nullptr;
};
struct QRhiMetalData;
class QRhiMetal : public QRhiImplementation
{
public:
QRhiMetal(QRhiMetalInitParams *params, QRhiMetalNativeHandles *importDevice = nullptr);
~QRhiMetal();
bool create(QRhi::Flags flags) override;
void destroy() override;
QRhiGraphicsPipeline *createGraphicsPipeline() override;
QRhiShaderResourceBindings *createShaderResourceBindings() override;
QRhiBuffer *createBuffer(QRhiBuffer::Type type,
QRhiBuffer::UsageFlags usage,
int size) override;
QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
const QSize &pixelSize,
int sampleCount,
QRhiRenderBuffer::Flags flags) override;
QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize,
int sampleCount,
QRhiTexture::Flags flags) override;
QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
QRhiSampler::Filter mipmapMode,
QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override;
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
QRhiTextureRenderTarget::Flags flags) override;
QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
QRhi::FrameOpResult endOffscreenFrame() override;
QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
void beginPass(QRhiCommandBuffer *cb,
QRhiRenderTarget *rt,
const QColor &colorClearValue,
const QRhiDepthStencilClearValue &depthStencilClearValue,
QRhiResourceUpdateBatch *resourceUpdates) override;
void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
void setGraphicsPipeline(QRhiCommandBuffer *cb,
QRhiGraphicsPipeline *ps) override;
void setShaderResources(QRhiCommandBuffer *cb,
QRhiShaderResourceBindings *srb,
int dynamicOffsetCount,
const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override;
void setVertexInput(QRhiCommandBuffer *cb,
int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
QRhiBuffer *indexBuf, quint32 indexOffset,
QRhiCommandBuffer::IndexFormat indexFormat) override;
void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override;
void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
quint32 instanceCount, quint32 firstIndex,
qint32 vertexOffset, quint32 firstInstance) override;
void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override;
void debugMarkEnd(QRhiCommandBuffer *cb) override;
void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override;
const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override;
void beginExternal(QRhiCommandBuffer *cb) override;
void endExternal(QRhiCommandBuffer *cb) override;
QVector<int> supportedSampleCounts() const override;
int ubufAlignment() const override;
bool isYUpInFramebuffer() const override;
bool isYUpInNDC() const override;
bool isClipDepthZeroToOne() const override;
QMatrix4x4 clipSpaceCorrMatrix() const override;
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override;
bool isFeatureSupported(QRhi::Feature feature) const override;
int resourceLimit(QRhi::ResourceLimit limit) const override;
const QRhiNativeHandles *nativeHandles() override;
void sendVMemStatsToProfiler() override;
void executeDeferredReleases(bool forced = false);
void finishActiveReadbacks(bool forced = false);
qsizetype subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const;
void enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEncPtr,
int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc,
qsizetype *curOfs);
void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
void executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD);
void enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, QMetalCommandBuffer *cbD,
int dynamicOffsetCount,
const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
bool offsetOnlyChange);
int effectiveSampleCount(int sampleCount) const;
bool importedDevice = false;
bool importedCmdQueue = false;
bool inFrame = false;
bool inPass = false;
QMetalSwapChain *currentSwapChain = nullptr;
QSet<QMetalSwapChain *> swapchains;
QRhiMetalNativeHandles nativeHandlesStruct;
struct {
int maxTextureSize = 4096;
} caps;
QRhiMetalData *d = nullptr;
};
QT_END_NAMESPACE
#endif

709
src/gui/rhi/qrhinull.cpp Normal file
View File

@ -0,0 +1,709 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qrhinull_p_p.h"
#include <qmath.h>
QT_BEGIN_NAMESPACE
/*!
\class QRhiNullInitParams
\inmodule QtRhi
\brief Null backend specific initialization parameters.
A Null QRhi needs no special parameters for initialization.
\badcode
QRhiNullInitParams params;
rhi = QRhi::create(QRhi::Null, &params);
\endcode
The Null backend does not issue any graphics calls and creates no
resources. All QRhi operations will succeed as normal so applications can
still be run, albeit potentially at an unthrottled speed, depending on
their frame rendering strategy. The backend reports resources to
QRhiProfiler as usual.
*/
/*!
\class QRhiNullNativeHandles
\inmodule QtRhi
\brief Empty.
*/
/*!
\class QRhiNullTextureNativeHandles
\inmodule QtRhi
\brief Empty.
*/
QRhiNull::QRhiNull(QRhiNullInitParams *params)
: offscreenCommandBuffer(this)
{
Q_UNUSED(params);
}
bool QRhiNull::create(QRhi::Flags flags)
{
Q_UNUSED(flags);
return true;
}
void QRhiNull::destroy()
{
}
QVector<int> QRhiNull::supportedSampleCounts() const
{
return { 1 };
}
QRhiSwapChain *QRhiNull::createSwapChain()
{
return new QNullSwapChain(this);
}
QRhiBuffer *QRhiNull::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
{
return new QNullBuffer(this, type, usage, size);
}
int QRhiNull::ubufAlignment() const
{
return 256;
}
bool QRhiNull::isYUpInFramebuffer() const
{
return false;
}
bool QRhiNull::isYUpInNDC() const
{
return true;
}
bool QRhiNull::isClipDepthZeroToOne() const
{
return true;
}
QMatrix4x4 QRhiNull::clipSpaceCorrMatrix() const
{
return QMatrix4x4(); // identity
}
bool QRhiNull::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
{
Q_UNUSED(format);
Q_UNUSED(flags);
return true;
}
bool QRhiNull::isFeatureSupported(QRhi::Feature feature) const
{
Q_UNUSED(feature);
return true;
}
int QRhiNull::resourceLimit(QRhi::ResourceLimit limit) const
{
switch (limit) {
case QRhi::TextureSizeMin:
return 1;
case QRhi::TextureSizeMax:
return 16384;
case QRhi::MaxColorAttachments:
return 8;
case QRhi::FramesInFlight:
return 2; // dummy
default:
Q_UNREACHABLE();
return 0;
}
}
const QRhiNativeHandles *QRhiNull::nativeHandles()
{
return &nativeHandlesStruct;
}
void QRhiNull::sendVMemStatsToProfiler()
{
// nothing to do here
}
QRhiRenderBuffer *QRhiNull::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags)
{
return new QNullRenderBuffer(this, type, pixelSize, sampleCount, flags);
}
QRhiTexture *QRhiNull::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
int sampleCount, QRhiTexture::Flags flags)
{
return new QNullTexture(this, format, pixelSize, sampleCount, flags);
}
QRhiSampler *QRhiNull::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
QRhiSampler::Filter mipmapMode,
QRhiSampler::AddressMode u, QRhiSampler::AddressMode v)
{
return new QNullSampler(this, magFilter, minFilter, mipmapMode, u, v);
}
QRhiTextureRenderTarget *QRhiNull::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
QRhiTextureRenderTarget::Flags flags)
{
return new QNullTextureRenderTarget(this, desc, flags);
}
QRhiGraphicsPipeline *QRhiNull::createGraphicsPipeline()
{
return new QNullGraphicsPipeline(this);
}
QRhiShaderResourceBindings *QRhiNull::createShaderResourceBindings()
{
return new QNullShaderResourceBindings(this);
}
void QRhiNull::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
{
Q_UNUSED(cb);
Q_UNUSED(ps);
}
void QRhiNull::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
int dynamicOffsetCount,
const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
{
Q_UNUSED(cb);
Q_UNUSED(srb);
Q_UNUSED(dynamicOffsetCount);
Q_UNUSED(dynamicOffsets);
}
void QRhiNull::setVertexInput(QRhiCommandBuffer *cb,
int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
{
Q_UNUSED(cb);
Q_UNUSED(startBinding);
Q_UNUSED(bindingCount);
Q_UNUSED(bindings);
Q_UNUSED(indexBuf);
Q_UNUSED(indexOffset);
Q_UNUSED(indexFormat);
}
void QRhiNull::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
{
Q_UNUSED(cb);
Q_UNUSED(viewport);
}
void QRhiNull::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
{
Q_UNUSED(cb);
Q_UNUSED(scissor);
}
void QRhiNull::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
{
Q_UNUSED(cb);
Q_UNUSED(c);
}
void QRhiNull::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
{
Q_UNUSED(cb);
Q_UNUSED(refValue);
}
void QRhiNull::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
{
Q_UNUSED(cb);
Q_UNUSED(vertexCount);
Q_UNUSED(instanceCount);
Q_UNUSED(firstVertex);
Q_UNUSED(firstInstance);
}
void QRhiNull::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
{
Q_UNUSED(cb);
Q_UNUSED(indexCount);
Q_UNUSED(instanceCount);
Q_UNUSED(firstIndex);
Q_UNUSED(vertexOffset);
Q_UNUSED(firstInstance);
}
void QRhiNull::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
{
Q_UNUSED(cb);
Q_UNUSED(name);
}
void QRhiNull::debugMarkEnd(QRhiCommandBuffer *cb)
{
Q_UNUSED(cb);
}
void QRhiNull::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
{
Q_UNUSED(cb);
Q_UNUSED(msg);
}
const QRhiNativeHandles *QRhiNull::nativeHandles(QRhiCommandBuffer *cb)
{
Q_UNUSED(cb);
return nullptr;
}
void QRhiNull::beginExternal(QRhiCommandBuffer *cb)
{
Q_UNUSED(cb);
}
void QRhiNull::endExternal(QRhiCommandBuffer *cb)
{
Q_UNUSED(cb);
}
QRhi::FrameOpResult QRhiNull::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
{
Q_UNUSED(flags);
currentSwapChain = swapChain;
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
QRHI_PROF_F(beginSwapChainFrame(swapChain));
return QRhi::FrameOpSuccess;
}
QRhi::FrameOpResult QRhiNull::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
{
Q_UNUSED(flags);
QNullSwapChain *swapChainD = QRHI_RES(QNullSwapChain, swapChain);
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
QRHI_PROF_F(swapChainFrameGpuTime(swapChain, 0.000666f));
swapChainD->frameCount += 1;
currentSwapChain = nullptr;
return QRhi::FrameOpSuccess;
}
QRhi::FrameOpResult QRhiNull::beginOffscreenFrame(QRhiCommandBuffer **cb)
{
*cb = &offscreenCommandBuffer;
return QRhi::FrameOpSuccess;
}
QRhi::FrameOpResult QRhiNull::endOffscreenFrame()
{
return QRhi::FrameOpSuccess;
}
QRhi::FrameOpResult QRhiNull::finish()
{
return QRhi::FrameOpSuccess;
}
void QRhiNull::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
{
Q_UNUSED(cb);
QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
QRhiReadbackResult *result = u.read.result;
QRhiTexture *tex = u.read.rb.texture();
if (tex) {
result->format = tex->format();
result->pixelSize = q->sizeForMipLevel(u.read.rb.level(), tex->pixelSize());
} else {
Q_ASSERT(currentSwapChain);
result->format = QRhiTexture::RGBA8;
result->pixelSize = currentSwapChain->currentPixelSize();
}
quint32 byteSize = 0;
textureFormatInfo(result->format, result->pixelSize, nullptr, &byteSize);
result->data.fill(0, byteSize);
if (result->completed)
result->completed();
}
}
ud->free();
}
void QRhiNull::beginPass(QRhiCommandBuffer *cb,
QRhiRenderTarget *rt,
const QColor &colorClearValue,
const QRhiDepthStencilClearValue &depthStencilClearValue,
QRhiResourceUpdateBatch *resourceUpdates)
{
Q_UNUSED(rt);
Q_UNUSED(colorClearValue);
Q_UNUSED(depthStencilClearValue);
if (resourceUpdates)
resourceUpdate(cb, resourceUpdates);
}
void QRhiNull::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
{
if (resourceUpdates)
resourceUpdate(cb, resourceUpdates);
}
QNullBuffer::QNullBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
: QRhiBuffer(rhi, type, usage, size)
{
}
QNullBuffer::~QNullBuffer()
{
release();
}
void QNullBuffer::release()
{
QRHI_PROF;
QRHI_PROF_F(releaseBuffer(this));
}
bool QNullBuffer::build()
{
QRHI_PROF;
QRHI_PROF_F(newBuffer(this, m_size, 1, 0));
return true;
}
QNullRenderBuffer::QNullRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags)
: QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags)
{
}
QNullRenderBuffer::~QNullRenderBuffer()
{
release();
}
void QNullRenderBuffer::release()
{
QRHI_PROF;
QRHI_PROF_F(releaseRenderBuffer(this));
}
bool QNullRenderBuffer::build()
{
QRHI_PROF;
QRHI_PROF_F(newRenderBuffer(this, false, false, 1));
return true;
}
QRhiTexture::Format QNullRenderBuffer::backingFormat() const
{
return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
}
QNullTexture::QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
int sampleCount, Flags flags)
: QRhiTexture(rhi, format, pixelSize, sampleCount, flags)
{
}
QNullTexture::~QNullTexture()
{
release();
}
void QNullTexture::release()
{
QRHI_PROF;
QRHI_PROF_F(releaseTexture(this));
}
bool QNullTexture::build()
{
const bool isCube = m_flags.testFlag(CubeMap);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const int mipLevelCount = hasMipMaps ? qCeil(log2(qMax(size.width(), size.height()))) + 1 : 1;
QRHI_PROF;
QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, 1));
return true;
}
bool QNullTexture::buildFrom(const QRhiNativeHandles *src)
{
Q_UNUSED(src);
const bool isCube = m_flags.testFlag(CubeMap);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const int mipLevelCount = hasMipMaps ? qCeil(log2(qMax(size.width(), size.height()))) + 1 : 1;
QRHI_PROF;
QRHI_PROF_F(newTexture(this, false, mipLevelCount, isCube ? 6 : 1, 1));
return true;
}
const QRhiNativeHandles *QNullTexture::nativeHandles()
{
return &nativeHandlesStruct;
}
QNullSampler::QNullSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
AddressMode u, AddressMode v)
: QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v)
{
}
QNullSampler::~QNullSampler()
{
release();
}
void QNullSampler::release()
{
}
bool QNullSampler::build()
{
return true;
}
QNullRenderPassDescriptor::QNullRenderPassDescriptor(QRhiImplementation *rhi)
: QRhiRenderPassDescriptor(rhi)
{
}
QNullRenderPassDescriptor::~QNullRenderPassDescriptor()
{
release();
}
void QNullRenderPassDescriptor::release()
{
}
QNullReferenceRenderTarget::QNullReferenceRenderTarget(QRhiImplementation *rhi)
: QRhiRenderTarget(rhi),
d(rhi)
{
}
QNullReferenceRenderTarget::~QNullReferenceRenderTarget()
{
release();
}
void QNullReferenceRenderTarget::release()
{
}
QSize QNullReferenceRenderTarget::pixelSize() const
{
return d.pixelSize;
}
float QNullReferenceRenderTarget::devicePixelRatio() const
{
return d.dpr;
}
int QNullReferenceRenderTarget::sampleCount() const
{
return 1;
}
QNullTextureRenderTarget::QNullTextureRenderTarget(QRhiImplementation *rhi,
const QRhiTextureRenderTargetDescription &desc,
Flags flags)
: QRhiTextureRenderTarget(rhi, desc, flags),
d(rhi)
{
}
QNullTextureRenderTarget::~QNullTextureRenderTarget()
{
release();
}
void QNullTextureRenderTarget::release()
{
}
QRhiRenderPassDescriptor *QNullTextureRenderTarget::newCompatibleRenderPassDescriptor()
{
return new QNullRenderPassDescriptor(m_rhi);
}
bool QNullTextureRenderTarget::build()
{
d.rp = QRHI_RES(QNullRenderPassDescriptor, m_renderPassDesc);
const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
if (!colorAttachments.isEmpty()) {
QRhiTexture *tex = colorAttachments.first().texture();
QRhiRenderBuffer *rb = colorAttachments.first().renderBuffer();
d.pixelSize = tex ? tex->pixelSize() : rb->pixelSize();
} else if (m_desc.depthStencilBuffer()) {
d.pixelSize = m_desc.depthStencilBuffer()->pixelSize();
} else if (m_desc.depthTexture()) {
d.pixelSize = m_desc.depthTexture()->pixelSize();
}
return true;
}
QSize QNullTextureRenderTarget::pixelSize() const
{
return d.pixelSize;
}
float QNullTextureRenderTarget::devicePixelRatio() const
{
return d.dpr;
}
int QNullTextureRenderTarget::sampleCount() const
{
return 1;
}
QNullShaderResourceBindings::QNullShaderResourceBindings(QRhiImplementation *rhi)
: QRhiShaderResourceBindings(rhi)
{
}
QNullShaderResourceBindings::~QNullShaderResourceBindings()
{
release();
}
void QNullShaderResourceBindings::release()
{
}
bool QNullShaderResourceBindings::build()
{
return true;
}
QNullGraphicsPipeline::QNullGraphicsPipeline(QRhiImplementation *rhi)
: QRhiGraphicsPipeline(rhi)
{
}
QNullGraphicsPipeline::~QNullGraphicsPipeline()
{
release();
}
void QNullGraphicsPipeline::release()
{
}
bool QNullGraphicsPipeline::build()
{
return true;
}
QNullCommandBuffer::QNullCommandBuffer(QRhiImplementation *rhi)
: QRhiCommandBuffer(rhi)
{
}
QNullCommandBuffer::~QNullCommandBuffer()
{
release();
}
void QNullCommandBuffer::release()
{
// nothing to do here
}
QNullSwapChain::QNullSwapChain(QRhiImplementation *rhi)
: QRhiSwapChain(rhi),
rt(rhi),
cb(rhi)
{
}
QNullSwapChain::~QNullSwapChain()
{
release();
}
void QNullSwapChain::release()
{
QRHI_PROF;
QRHI_PROF_F(releaseSwapChain(this));
}
QRhiCommandBuffer *QNullSwapChain::currentFrameCommandBuffer()
{
return &cb;
}
QRhiRenderTarget *QNullSwapChain::currentFrameRenderTarget()
{
return &rt;
}
QSize QNullSwapChain::surfacePixelSize()
{
return QSize(1280, 720);
}
QRhiRenderPassDescriptor *QNullSwapChain::newCompatibleRenderPassDescriptor()
{
return new QNullRenderPassDescriptor(m_rhi);
}
bool QNullSwapChain::buildOrResize()
{
m_currentPixelSize = surfacePixelSize();
rt.d.rp = QRHI_RES(QNullRenderPassDescriptor, m_renderPassDesc);
rt.d.pixelSize = m_currentPixelSize;
frameCount = 0;
QRHI_PROF;
QRHI_PROF_F(resizeSwapChain(this, 1, 0, 1));
return true;
}
QT_END_NAMESPACE

69
src/gui/rhi/qrhinull_p.h Normal file
View File

@ -0,0 +1,69 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QRHINULL_H
#define QRHINULL_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <private/qrhi_p.h>
QT_BEGIN_NAMESPACE
struct Q_GUI_EXPORT QRhiNullInitParams : public QRhiInitParams
{
};
struct Q_GUI_EXPORT QRhiNullNativeHandles : public QRhiNativeHandles
{
};
struct Q_GUI_EXPORT QRhiNullTextureNativeHandles : public QRhiNativeHandles
{
};
QT_END_NAMESPACE
#endif

279
src/gui/rhi/qrhinull_p_p.h Normal file
View File

@ -0,0 +1,279 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QRHINULL_P_H
#define QRHINULL_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qrhinull_p.h"
#include "qrhi_p_p.h"
QT_BEGIN_NAMESPACE
struct QNullBuffer : public QRhiBuffer
{
QNullBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size);
~QNullBuffer();
void release() override;
bool build() override;
};
struct QNullRenderBuffer : public QRhiRenderBuffer
{
QNullRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags);
~QNullRenderBuffer();
void release() override;
bool build() override;
QRhiTexture::Format backingFormat() const override;
};
struct QNullTexture : public QRhiTexture
{
QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
int sampleCount, Flags flags);
~QNullTexture();
void release() override;
bool build() override;
bool buildFrom(const QRhiNativeHandles *src) override;
const QRhiNativeHandles *nativeHandles() override;
QRhiNullTextureNativeHandles nativeHandlesStruct;
};
struct QNullSampler : public QRhiSampler
{
QNullSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
AddressMode u, AddressMode v);
~QNullSampler();
void release() override;
bool build() override;
};
struct QNullRenderPassDescriptor : public QRhiRenderPassDescriptor
{
QNullRenderPassDescriptor(QRhiImplementation *rhi);
~QNullRenderPassDescriptor();
void release() override;
};
struct QNullRenderTargetData
{
QNullRenderTargetData(QRhiImplementation *) { }
QNullRenderPassDescriptor *rp = nullptr;
QSize pixelSize;
float dpr = 1;
};
struct QNullReferenceRenderTarget : public QRhiRenderTarget
{
QNullReferenceRenderTarget(QRhiImplementation *rhi);
~QNullReferenceRenderTarget();
void release() override;
QSize pixelSize() const override;
float devicePixelRatio() const override;
int sampleCount() const override;
QNullRenderTargetData d;
};
struct QNullTextureRenderTarget : public QRhiTextureRenderTarget
{
QNullTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags);
~QNullTextureRenderTarget();
void release() override;
QSize pixelSize() const override;
float devicePixelRatio() const override;
int sampleCount() const override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
bool build() override;
QNullRenderTargetData d;
};
struct QNullShaderResourceBindings : public QRhiShaderResourceBindings
{
QNullShaderResourceBindings(QRhiImplementation *rhi);
~QNullShaderResourceBindings();
void release() override;
bool build() override;
};
struct QNullGraphicsPipeline : public QRhiGraphicsPipeline
{
QNullGraphicsPipeline(QRhiImplementation *rhi);
~QNullGraphicsPipeline();
void release() override;
bool build() override;
};
struct QNullCommandBuffer : public QRhiCommandBuffer
{
QNullCommandBuffer(QRhiImplementation *rhi);
~QNullCommandBuffer();
void release() override;
};
struct QNullSwapChain : public QRhiSwapChain
{
QNullSwapChain(QRhiImplementation *rhi);
~QNullSwapChain();
void release() override;
QRhiCommandBuffer *currentFrameCommandBuffer() override;
QRhiRenderTarget *currentFrameRenderTarget() override;
QSize surfacePixelSize() override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
bool buildOrResize() override;
QNullReferenceRenderTarget rt;
QNullCommandBuffer cb;
int frameCount = 0;
};
class QRhiNull : public QRhiImplementation
{
public:
QRhiNull(QRhiNullInitParams *params);
bool create(QRhi::Flags flags) override;
void destroy() override;
QRhiGraphicsPipeline *createGraphicsPipeline() override;
QRhiShaderResourceBindings *createShaderResourceBindings() override;
QRhiBuffer *createBuffer(QRhiBuffer::Type type,
QRhiBuffer::UsageFlags usage,
int size) override;
QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
const QSize &pixelSize,
int sampleCount,
QRhiRenderBuffer::Flags flags) override;
QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize,
int sampleCount,
QRhiTexture::Flags flags) override;
QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
QRhiSampler::Filter mipmapMode,
QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override;
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
QRhiTextureRenderTarget::Flags flags) override;
QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
QRhi::FrameOpResult endOffscreenFrame() override;
QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
void beginPass(QRhiCommandBuffer *cb,
QRhiRenderTarget *rt,
const QColor &colorClearValue,
const QRhiDepthStencilClearValue &depthStencilClearValue,
QRhiResourceUpdateBatch *resourceUpdates) override;
void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
void setGraphicsPipeline(QRhiCommandBuffer *cb,
QRhiGraphicsPipeline *ps) override;
void setShaderResources(QRhiCommandBuffer *cb,
QRhiShaderResourceBindings *srb,
int dynamicOffsetCount,
const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override;
void setVertexInput(QRhiCommandBuffer *cb,
int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
QRhiBuffer *indexBuf, quint32 indexOffset,
QRhiCommandBuffer::IndexFormat indexFormat) override;
void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override;
void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
quint32 instanceCount, quint32 firstIndex,
qint32 vertexOffset, quint32 firstInstance) override;
void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override;
void debugMarkEnd(QRhiCommandBuffer *cb) override;
void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override;
const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override;
void beginExternal(QRhiCommandBuffer *cb) override;
void endExternal(QRhiCommandBuffer *cb) override;
QVector<int> supportedSampleCounts() const override;
int ubufAlignment() const override;
bool isYUpInFramebuffer() const override;
bool isYUpInNDC() const override;
bool isClipDepthZeroToOne() const override;
QMatrix4x4 clipSpaceCorrMatrix() const override;
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override;
bool isFeatureSupported(QRhi::Feature feature) const override;
int resourceLimit(QRhi::ResourceLimit limit) const override;
const QRhiNativeHandles *nativeHandles() override;
void sendVMemStatsToProfiler() override;
QRhiNullNativeHandles nativeHandlesStruct;
QRhiSwapChain *currentSwapChain = nullptr;
QNullCommandBuffer offscreenCommandBuffer;
};
QT_END_NAMESPACE
#endif

View File

@ -0,0 +1,603 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qrhiprofiler_p_p.h"
#include "qrhi_p_p.h"
QT_BEGIN_NAMESPACE
/*!
\class QRhiProfiler
\inmodule QtRhi
\brief Collects resource and timing information from an active QRhi.
A QRhiProfiler is present for each QRhi. Query it via QRhi::profiler(). The
profiler is active only when the QRhi was created with
QRhi::EnableProfiling. No data is collected otherwise.
\note GPU timings are only available when QRhi indicates that
QRhi::Timestamps is supported.
Besides collecting data from the QRhi implementations, some additional
values are calculated. For example, for textures and similar resources the
profiler gives an estimate of the complete amount of memory the resource
needs.
\section2 Output Format
The output is comma-separated text. Each line has a number of
comma-separated entries and each line ends with a comma.
For example:
\badcode
1,0,140446057946208,Triangle vbuf,type,0,usage,1,logical_size,84,effective_size,84,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
1,0,140446057947376,Triangle ubuf,type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
1,1,140446057950416,,type,0,usage,1,logical_size,112,effective_size,112,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
1,1,140446057950544,,type,0,usage,2,logical_size,12,effective_size,12,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
1,1,140446057947440,,type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
1,1,140446057984784,Cube vbuf (textured),type,0,usage,1,logical_size,720,effective_size,720,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
1,1,140446057982528,Cube ubuf (textured),type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
7,8,140446058913648,Qt texture,width,256,height,256,format,1,owns_native_resource,1,mip_count,9,layer_count,1,effective_sample_count,1,approx_byte_size,349524,
1,8,140446058795856,Cube vbuf (textured with offscreen),type,0,usage,1,logical_size,720,effective_size,720,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
1,8,140446058947920,Cube ubuf (textured with offscreen),type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
7,8,140446058794928,Texture for offscreen content,width,512,height,512,format,1,owns_native_resource,1,mip_count,1,layer_count,1,effective_sample_count,1,approx_byte_size,1048576,
1,8,140446058963904,Triangle vbuf,type,0,usage,1,logical_size,84,effective_size,84,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
1,8,140446058964560,Triangle ubuf,type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
5,9,140446057945392,,type,0,width,1280,height,720,effective_sample_count,1,transient_backing,0,winsys_backing,0,approx_byte_size,3686400,
11,9,140446057944592,,width,1280,height,720,buffer_count,2,msaa_buffer_count,0,effective_sample_count,1,approx_total_byte_size,7372800,
9,9,140446058913648,Qt texture,slot,0,size,262144,
10,9,140446058913648,Qt texture,slot,0,
17,2019,140446057944592,,frames_since_resize,121,min_ms_frame_delta,9,max_ms_frame_delta,33,Favg_ms_frame_delta,16.1167,
18,2019,140446057944592,,frames_since_resize,121,min_ms_frame_build,0,max_ms_frame_build,1,Favg_ms_frame_build,0.00833333,
17,4019,140446057944592,,frames_since_resize,241,min_ms_frame_delta,15,max_ms_frame_delta,17,Favg_ms_frame_delta,16.0583,
18,4019,140446057944592,,frames_since_resize,241,min_ms_frame_build,0,max_ms_frame_build,0,Favg_ms_frame_build,0,
12,5070,140446057944592,,
2,5079,140446057947376,Triangle ubuf,
2,5079,140446057946208,Triangle vbuf,
2,5079,140446057947440,,
2,5079,140446057950544,,
2,5079,140446057950416,,
8,5079,140446058913648,Qt texture,
2,5079,140446057982528,Cube ubuf (textured),
2,5079,140446057984784,Cube vbuf (textured),
2,5079,140446058964560,Triangle ubuf,
2,5079,140446058963904,Triangle vbuf,
8,5079,140446058794928,Texture for offscreen content,
2,5079,140446058947920,Cube ubuf (textured with offscreen),
2,5079,140446058795856,Cube vbuf (textured with offscreen),
6,5079,140446057945392,,
\endcode
Each line starts with \c op, \c timestamp, \c res, \c name where op is a
value from StreamOp, timestamp is a recording timestamp in milliseconds
(qint64), res is a number (quint64) referring to the QRhiResource the entry
refers to, or 0 if not applicable. \c name is the value of
QRhiResource::name() and may be empty as well. The \c name will never
contain a comma.
This is followed by any number of \c{key, value} pairs where \c key is an
unspecified string and \c value is a number. If \c key starts with \c F, it
indicates the value is a float. Otherwise assume that the value is a
qint64.
*/
/*!
\enum QRhiProfiler::StreamOp
Describes an entry in the profiler's output stream.
\value NewBuffer A buffer is created
\value ReleaseBuffer A buffer is destroyed
\value NewBufferStagingArea A staging buffer for buffer upload is created
\value ReleaseBufferStagingArea A staging buffer for buffer upload is destroyed
\value NewRenderBuffer A renderbuffer is created
\value ReleaseRenderBuffer A renderbuffer is destroyed
\value NewTexture A texture is created
\value ReleaseTexture A texture is destroyed
\value NewTextureStagingArea A staging buffer for texture upload is created
\value ReleaseTextureStagingArea A staging buffer for texture upload is destroyed
\value ResizeSwapChain A swapchain is created or resized
\value ReleaseSwapChain A swapchain is destroyed
\value NewReadbackBuffer A staging buffer for readback is created
\value ReleaseReadbackBuffer A staging buffer for readback is destroyed
\value GpuMemAllocStats GPU memory allocator statistics
\value GpuFrameTime GPU frame times
\value FrameToFrameTime CPU frame-to-frame times
\value FrameBuildTime CPU beginFrame-endFrame times
*/
/*!
\class QRhiProfiler::CpuTime
\inmodule QtRhi
\brief Contains CPU-side frame timings.
Once sufficient number of frames have been rendered, the minimum, maximum,
and average values (in milliseconds) from various measurements are made
available in this struct queriable from QRhiProfiler::frameToFrameTimes()
and QRhiProfiler::frameBuildTimes().
\sa QRhiProfiler::setFrameTimingWriteInterval()
*/
/*!
\class QRhiProfiler::GpuTime
\inmodule QtRhi
\brief Contains GPU-side frame timings.
Once sufficient number of frames have been rendered, the minimum, maximum,
and average values (in milliseconds) calculated from GPU command buffer
timestamps are made available in this struct queriable from
QRhiProfiler::gpuFrameTimes().
\sa QRhiProfiler::setFrameTimingWriteInterval()
*/
/*!
\internal
*/
QRhiProfiler::QRhiProfiler()
: d(new QRhiProfilerPrivate)
{
d->ts.start();
}
/*!
Destructor.
*/
QRhiProfiler::~QRhiProfiler()
{
// Flush because there is a high chance we have writes that were made since
// the event loop last ran. (esp. relevant for network devices like QTcpSocket)
if (d->outputDevice)
d->outputDevice->waitForBytesWritten(1000);
delete d;
}
/*!
Sets the output \a device.
\note No output will be generated when QRhi::EnableProfiling was not set.
*/
void QRhiProfiler::setDevice(QIODevice *device)
{
d->outputDevice = device;
}
/*!
Requests writing a GpuMemAllocStats entry into the output, when applicable.
Backends that do not support this will ignore the request. This is an
explicit request since getting the allocator status and statistics may be
an expensive operation.
*/
void QRhiProfiler::addVMemAllocatorStats()
{
if (d->rhiDWhenEnabled)
d->rhiDWhenEnabled->sendVMemStatsToProfiler();
}
/*!
\return the currently set frame timing writeout interval.
*/
int QRhiProfiler::frameTimingWriteInterval() const
{
return d->frameTimingWriteInterval;
}
/*!
Sets the number of frames that need to be rendered before the collected CPU
and GPU timings are processed (min, max, average are calculated) to \a
frameCount.
The default value is 120.
*/
void QRhiProfiler::setFrameTimingWriteInterval(int frameCount)
{
if (frameCount > 0)
d->frameTimingWriteInterval = frameCount;
}
/*!
\return min, max, and avg in milliseconds for the time that elapsed between two
QRhi::endFrame() calls.
\note The values are all 0 until at least frameTimingWriteInterval() frames
have been rendered.
*/
QRhiProfiler::CpuTime QRhiProfiler::frameToFrameTimes(QRhiSwapChain *sc) const
{
auto it = d->swapchains.constFind(sc);
if (it != d->swapchains.constEnd())
return it->frameToFrameTime;
return QRhiProfiler::CpuTime();
}
/*!
\return min, max, and avg in milliseconds for the time that elapsed between
a QRhi::beginFrame() and QRhi::endFrame().
\note The values are all 0 until at least frameTimingWriteInterval() frames
have been rendered.
*/
QRhiProfiler::CpuTime QRhiProfiler::frameBuildTimes(QRhiSwapChain *sc) const
{
auto it = d->swapchains.constFind(sc);
if (it != d->swapchains.constEnd())
return it->beginToEndFrameTime;
return QRhiProfiler::CpuTime();
}
/*!
\return min, max, and avg in milliseconds for the GPU time that is spent on
one frame.
\note The values are all 0 until at least frameTimingWriteInterval() frames
have been rendered.
The GPU times should only be compared between runs on the same GPU of the
same system with the same backend. Comparing times for different graphics
cards or for different backends can give misleading results. The numbers are
not meant to be comparable that way.
\note Some backends have no support for this, and even for those that have,
it is not guaranteed that the driver will support it at run time. Support
can be checked via QRhi::Timestamps.
*/
QRhiProfiler::GpuTime QRhiProfiler::gpuFrameTimes(QRhiSwapChain *sc) const
{
auto it = d->swapchains.constFind(sc);
if (it != d->swapchains.constEnd())
return it->gpuFrameTime;
return QRhiProfiler::GpuTime();
}
void QRhiProfilerPrivate::startEntry(QRhiProfiler::StreamOp op, qint64 timestamp, QRhiResource *res)
{
buf.clear();
buf.append(QByteArray::number(op));
buf.append(',');
buf.append(QByteArray::number(timestamp));
buf.append(',');
buf.append(QByteArray::number(quint64(quintptr(res))));
buf.append(',');
if (res)
buf.append(res->name());
buf.append(',');
}
void QRhiProfilerPrivate::writeInt(const char *key, qint64 v)
{
Q_ASSERT(key[0] != 'F');
buf.append(key);
buf.append(',');
buf.append(QByteArray::number(v));
buf.append(',');
}
void QRhiProfilerPrivate::writeFloat(const char *key, float f)
{
Q_ASSERT(key[0] == 'F');
buf.append(key);
buf.append(',');
buf.append(QByteArray::number(f));
buf.append(',');
}
void QRhiProfilerPrivate::endEntry()
{
buf.append('\n');
outputDevice->write(buf);
}
void QRhiProfilerPrivate::newBuffer(QRhiBuffer *buf, quint32 realSize, int backingGpuBufCount, int backingCpuBufCount)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::NewBuffer, ts.elapsed(), buf);
writeInt("type", buf->type());
writeInt("usage", buf->usage());
writeInt("logical_size", buf->size());
writeInt("effective_size", realSize);
writeInt("backing_gpu_buf_count", backingGpuBufCount);
writeInt("backing_cpu_buf_count", backingCpuBufCount);
endEntry();
}
void QRhiProfilerPrivate::releaseBuffer(QRhiBuffer *buf)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::ReleaseBuffer, ts.elapsed(), buf);
endEntry();
}
void QRhiProfilerPrivate::newBufferStagingArea(QRhiBuffer *buf, int slot, quint32 size)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::NewBufferStagingArea, ts.elapsed(), buf);
writeInt("slot", slot);
writeInt("size", size);
endEntry();
}
void QRhiProfilerPrivate::releaseBufferStagingArea(QRhiBuffer *buf, int slot)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::ReleaseBufferStagingArea, ts.elapsed(), buf);
writeInt("slot", slot);
endEntry();
}
void QRhiProfilerPrivate::newRenderBuffer(QRhiRenderBuffer *rb, bool transientBacking, bool winSysBacking, int sampleCount)
{
if (!outputDevice)
return;
const QRhiRenderBuffer::Type type = rb->type();
const QSize sz = rb->pixelSize();
// just make up something, ds is likely D24S8 while color is RGBA8 or similar
const QRhiTexture::Format assumedFormat = type == QRhiRenderBuffer::DepthStencil ? QRhiTexture::D32F : QRhiTexture::RGBA8;
quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(assumedFormat, sz, 1, 1);
if (sampleCount > 1)
byteSize *= sampleCount;
startEntry(QRhiProfiler::NewRenderBuffer, ts.elapsed(), rb);
writeInt("type", type);
writeInt("width", sz.width());
writeInt("height", sz.height());
writeInt("effective_sample_count", sampleCount);
writeInt("transient_backing", transientBacking);
writeInt("winsys_backing", winSysBacking);
writeInt("approx_byte_size", byteSize);
endEntry();
}
void QRhiProfilerPrivate::releaseRenderBuffer(QRhiRenderBuffer *rb)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::ReleaseRenderBuffer, ts.elapsed(), rb);
endEntry();
}
void QRhiProfilerPrivate::newTexture(QRhiTexture *tex, bool owns, int mipCount, int layerCount, int sampleCount)
{
if (!outputDevice)
return;
const QRhiTexture::Format format = tex->format();
const QSize sz = tex->pixelSize();
quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(format, sz, mipCount, layerCount);
if (sampleCount > 1)
byteSize *= sampleCount;
startEntry(QRhiProfiler::NewTexture, ts.elapsed(), tex);
writeInt("width", sz.width());
writeInt("height", sz.height());
writeInt("format", format);
writeInt("owns_native_resource", owns);
writeInt("mip_count", mipCount);
writeInt("layer_count", layerCount);
writeInt("effective_sample_count", sampleCount);
writeInt("approx_byte_size", byteSize);
endEntry();
}
void QRhiProfilerPrivate::releaseTexture(QRhiTexture *tex)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::ReleaseTexture, ts.elapsed(), tex);
endEntry();
}
void QRhiProfilerPrivate::newTextureStagingArea(QRhiTexture *tex, int slot, quint32 size)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::NewTextureStagingArea, ts.elapsed(), tex);
writeInt("slot", slot);
writeInt("size", size);
endEntry();
}
void QRhiProfilerPrivate::releaseTextureStagingArea(QRhiTexture *tex, int slot)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::ReleaseTextureStagingArea, ts.elapsed(), tex);
writeInt("slot", slot);
endEntry();
}
void QRhiProfilerPrivate::resizeSwapChain(QRhiSwapChain *sc, int bufferCount, int msaaBufferCount, int sampleCount)
{
if (!outputDevice)
return;
const QSize sz = sc->currentPixelSize();
quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(QRhiTexture::BGRA8, sz, 1, 1);
byteSize = byteSize * bufferCount + byteSize * msaaBufferCount * sampleCount;
startEntry(QRhiProfiler::ResizeSwapChain, ts.elapsed(), sc);
writeInt("width", sz.width());
writeInt("height", sz.height());
writeInt("buffer_count", bufferCount);
writeInt("msaa_buffer_count", msaaBufferCount);
writeInt("effective_sample_count", sampleCount);
writeInt("approx_total_byte_size", byteSize);
endEntry();
}
void QRhiProfilerPrivate::releaseSwapChain(QRhiSwapChain *sc)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::ReleaseSwapChain, ts.elapsed(), sc);
endEntry();
}
template<typename T>
void calcTiming(QVector<T> *vec, T *minDelta, T *maxDelta, float *avgDelta)
{
if (vec->isEmpty())
return;
*minDelta = *maxDelta = 0;
float totalDelta = 0;
for (T delta : qAsConst(*vec)) {
totalDelta += float(delta);
if (*minDelta == 0 || delta < *minDelta)
*minDelta = delta;
if (*maxDelta == 0 || delta > *maxDelta)
*maxDelta = delta;
}
*avgDelta = totalDelta / vec->count();
vec->clear();
}
void QRhiProfilerPrivate::beginSwapChainFrame(QRhiSwapChain *sc)
{
Sc &scd(swapchains[sc]);
scd.beginToEndTimer.start();
}
void QRhiProfilerPrivate::endSwapChainFrame(QRhiSwapChain *sc, int frameCount)
{
Sc &scd(swapchains[sc]);
if (!scd.frameToFrameRunning) {
scd.frameToFrameTimer.start();
scd.frameToFrameRunning = true;
return;
}
scd.frameToFrameSamples.append(scd.frameToFrameTimer.restart());
if (scd.frameToFrameSamples.count() >= frameTimingWriteInterval) {
calcTiming(&scd.frameToFrameSamples,
&scd.frameToFrameTime.minTime, &scd.frameToFrameTime.maxTime, &scd.frameToFrameTime.avgTime);
if (outputDevice) {
startEntry(QRhiProfiler::FrameToFrameTime, ts.elapsed(), sc);
writeInt("frames_since_resize", frameCount);
writeInt("min_ms_frame_delta", scd.frameToFrameTime.minTime);
writeInt("max_ms_frame_delta", scd.frameToFrameTime.maxTime);
writeFloat("Favg_ms_frame_delta", scd.frameToFrameTime.avgTime);
endEntry();
}
}
scd.beginToEndSamples.append(scd.beginToEndTimer.elapsed());
if (scd.beginToEndSamples.count() >= frameTimingWriteInterval) {
calcTiming(&scd.beginToEndSamples,
&scd.beginToEndFrameTime.minTime, &scd.beginToEndFrameTime.maxTime, &scd.beginToEndFrameTime.avgTime);
if (outputDevice) {
startEntry(QRhiProfiler::FrameBuildTime, ts.elapsed(), sc);
writeInt("frames_since_resize", frameCount);
writeInt("min_ms_frame_build", scd.beginToEndFrameTime.minTime);
writeInt("max_ms_frame_build", scd.beginToEndFrameTime.maxTime);
writeFloat("Favg_ms_frame_build", scd.beginToEndFrameTime.avgTime);
endEntry();
}
}
}
void QRhiProfilerPrivate::swapChainFrameGpuTime(QRhiSwapChain *sc, float gpuTime)
{
Sc &scd(swapchains[sc]);
scd.gpuFrameSamples.append(gpuTime);
if (scd.gpuFrameSamples.count() >= frameTimingWriteInterval) {
calcTiming(&scd.gpuFrameSamples,
&scd.gpuFrameTime.minTime, &scd.gpuFrameTime.maxTime, &scd.gpuFrameTime.avgTime);
if (outputDevice) {
startEntry(QRhiProfiler::GpuFrameTime, ts.elapsed(), sc);
writeFloat("Fmin_ms_gpu_frame_time", scd.gpuFrameTime.minTime);
writeFloat("Fmax_ms_gpu_frame_time", scd.gpuFrameTime.maxTime);
writeFloat("Favg_ms_gpu_frame_time", scd.gpuFrameTime.avgTime);
endEntry();
}
}
}
void QRhiProfilerPrivate::newReadbackBuffer(quint64 id, QRhiResource *src, quint32 size)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::NewReadbackBuffer, ts.elapsed(), src);
writeInt("id", id);
writeInt("size", size);
endEntry();
}
void QRhiProfilerPrivate::releaseReadbackBuffer(quint64 id)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::ReleaseReadbackBuffer, ts.elapsed(), nullptr);
writeInt("id", id);
endEntry();
}
void QRhiProfilerPrivate::vmemStat(int realAllocCount, int subAllocCount, quint32 totalSize, quint32 unusedSize)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::GpuMemAllocStats, ts.elapsed(), nullptr);
writeInt("real_alloc_count", realAllocCount);
writeInt("sub_alloc_count", subAllocCount);
writeInt("total_size", totalSize);
writeInt("unused_size", unusedSize);
endEntry();
}
QT_END_NAMESPACE

View File

@ -0,0 +1,120 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QRHIPROFILER_H
#define QRHIPROFILER_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <private/qrhi_p.h>
QT_BEGIN_NAMESPACE
class QRhiProfilerPrivate;
class QIODevice;
class Q_GUI_EXPORT QRhiProfiler
{
public:
enum StreamOp {
NewBuffer = 1,
ReleaseBuffer,
NewBufferStagingArea,
ReleaseBufferStagingArea,
NewRenderBuffer,
ReleaseRenderBuffer,
NewTexture,
ReleaseTexture,
NewTextureStagingArea,
ReleaseTextureStagingArea,
ResizeSwapChain,
ReleaseSwapChain,
NewReadbackBuffer,
ReleaseReadbackBuffer,
GpuMemAllocStats,
GpuFrameTime,
FrameToFrameTime,
FrameBuildTime
};
~QRhiProfiler();
void setDevice(QIODevice *device);
void addVMemAllocatorStats();
int frameTimingWriteInterval() const;
void setFrameTimingWriteInterval(int frameCount);
struct CpuTime {
qint64 minTime = 0;
qint64 maxTime = 0;
float avgTime = 0;
};
struct GpuTime {
float minTime = 0;
float maxTime = 0;
float avgTime = 0;
};
CpuTime frameToFrameTimes(QRhiSwapChain *sc) const;
CpuTime frameBuildTimes(QRhiSwapChain *sc) const; // beginFrame - endFrame
GpuTime gpuFrameTimes(QRhiSwapChain *sc) const;
private:
Q_DISABLE_COPY(QRhiProfiler)
QRhiProfiler();
QRhiProfilerPrivate *d;
friend class QRhiImplementation;
friend class QRhiProfilerPrivate;
};
Q_DECLARE_TYPEINFO(QRhiProfiler::CpuTime, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QRhiProfiler::GpuTime, Q_MOVABLE_TYPE);
QT_END_NAMESPACE
#endif

View File

@ -0,0 +1,121 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QRHIPROFILER_P_H
#define QRHIPROFILER_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qrhiprofiler_p.h"
#include <QElapsedTimer>
#include <QHash>
QT_BEGIN_NAMESPACE
class QRhiProfilerPrivate
{
public:
static QRhiProfilerPrivate *get(QRhiProfiler *p) { return p->d; }
void newBuffer(QRhiBuffer *buf, quint32 realSize, int backingGpuBufCount, int backingCpuBufCount);
void releaseBuffer(QRhiBuffer *buf);
void newBufferStagingArea(QRhiBuffer *buf, int slot, quint32 size);
void releaseBufferStagingArea(QRhiBuffer *buf, int slot);
void newRenderBuffer(QRhiRenderBuffer *rb, bool transientBacking, bool winSysBacking, int sampleCount);
void releaseRenderBuffer(QRhiRenderBuffer *rb);
void newTexture(QRhiTexture *tex, bool owns, int mipCount, int layerCount, int sampleCount);
void releaseTexture(QRhiTexture *tex);
void newTextureStagingArea(QRhiTexture *tex, int slot, quint32 size);
void releaseTextureStagingArea(QRhiTexture *tex, int slot);
void resizeSwapChain(QRhiSwapChain *sc, int bufferCount, int msaaBufferCount, int sampleCount);
void releaseSwapChain(QRhiSwapChain *sc);
void beginSwapChainFrame(QRhiSwapChain *sc);
void endSwapChainFrame(QRhiSwapChain *sc, int frameCount);
void swapChainFrameGpuTime(QRhiSwapChain *sc, float gpuTimeMs);
void newReadbackBuffer(quint64 id, QRhiResource *src, quint32 size);
void releaseReadbackBuffer(quint64 id);
void vmemStat(int realAllocCount, int subAllocCount, quint32 totalSize, quint32 unusedSize);
void startEntry(QRhiProfiler::StreamOp op, qint64 timestamp, QRhiResource *res);
void writeInt(const char *key, qint64 v);
void writeFloat(const char *key, float f);
void endEntry();
QRhiImplementation *rhiDWhenEnabled = nullptr;
QIODevice *outputDevice = nullptr;
QElapsedTimer ts;
QByteArray buf;
static const int DEFAULT_FRAME_TIMING_WRITE_INTERVAL = 120; // frames
int frameTimingWriteInterval = DEFAULT_FRAME_TIMING_WRITE_INTERVAL;
struct Sc {
Sc() {
frameToFrameSamples.reserve(DEFAULT_FRAME_TIMING_WRITE_INTERVAL);
beginToEndSamples.reserve(DEFAULT_FRAME_TIMING_WRITE_INTERVAL);
gpuFrameSamples.reserve(DEFAULT_FRAME_TIMING_WRITE_INTERVAL);
}
QElapsedTimer frameToFrameTimer;
bool frameToFrameRunning = false;
QElapsedTimer beginToEndTimer;
QVector<qint64> frameToFrameSamples;
QVector<qint64> beginToEndSamples;
QVector<float> gpuFrameSamples;
QRhiProfiler::CpuTime frameToFrameTime;
QRhiProfiler::CpuTime beginToEndFrameTime;
QRhiProfiler::GpuTime gpuFrameTime;
};
QHash<QRhiSwapChain *, Sc> swapchains;
};
Q_DECLARE_TYPEINFO(QRhiProfilerPrivate::Sc, Q_MOVABLE_TYPE);
QT_END_NAMESPACE
#endif

5681
src/gui/rhi/qrhivulkan.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,85 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QRHIVULKAN_H
#define QRHIVULKAN_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <private/qrhi_p.h>
#include <QtGui/qvulkaninstance.h> // this is where vulkan.h gets pulled in
QT_BEGIN_NAMESPACE
struct Q_GUI_EXPORT QRhiVulkanInitParams : public QRhiInitParams
{
QVulkanInstance *inst = nullptr;
QWindow *window = nullptr;
};
struct Q_GUI_EXPORT QRhiVulkanNativeHandles : public QRhiNativeHandles
{
VkPhysicalDevice physDev = VK_NULL_HANDLE;
VkDevice dev = VK_NULL_HANDLE;
int gfxQueueFamilyIdx = -1;
VkQueue gfxQueue = VK_NULL_HANDLE;
VkCommandPool cmdPool = VK_NULL_HANDLE;
void *vmemAllocator = nullptr;
};
struct Q_GUI_EXPORT QRhiVulkanTextureNativeHandles : public QRhiNativeHandles
{
VkImage image = VK_NULL_HANDLE;
VkImageLayout layout = VK_IMAGE_LAYOUT_GENERAL;
};
struct Q_GUI_EXPORT QRhiVulkanCommandBufferNativeHandles : public QRhiNativeHandles
{
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
};
QT_END_NAMESPACE
#endif

View File

@ -0,0 +1,859 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QRHIVULKAN_P_H
#define QRHIVULKAN_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qrhivulkan_p.h"
#include "qrhi_p_p.h"
QT_BEGIN_NAMESPACE
class QVulkanFunctions;
class QVulkanDeviceFunctions;
static const int QVK_FRAMES_IN_FLIGHT = 2;
static const int QVK_DESC_SETS_PER_POOL = 128;
static const int QVK_UNIFORM_BUFFERS_PER_POOL = 256;
static const int QVK_COMBINED_IMAGE_SAMPLERS_PER_POOL = 256;
static const int QVK_MAX_ACTIVE_TIMESTAMP_PAIRS = 16;
// no vk_mem_alloc.h available here, void* is good enough
typedef void * QVkAlloc;
typedef void * QVkAllocator;
struct QVkBuffer : public QRhiBuffer
{
QVkBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size);
~QVkBuffer();
void release() override;
bool build() override;
VkBuffer buffers[QVK_FRAMES_IN_FLIGHT];
QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT];
QVector<QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate> pendingDynamicUpdates[QVK_FRAMES_IN_FLIGHT];
VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
struct UsageState {
VkAccessFlags access = 0;
VkPipelineStageFlags stage = 0;
};
UsageState usageState[QVK_FRAMES_IN_FLIGHT];
int lastActiveFrameSlot = -1;
uint generation = 0;
friend class QRhiVulkan;
};
struct QVkTexture;
struct QVkRenderBuffer : public QRhiRenderBuffer
{
QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
int sampleCount, Flags flags);
~QVkRenderBuffer();
void release() override;
bool build() override;
QRhiTexture::Format backingFormat() const override;
VkDeviceMemory memory = VK_NULL_HANDLE;
VkImage image = VK_NULL_HANDLE;
VkImageView imageView = VK_NULL_HANDLE;
VkSampleCountFlagBits samples;
QVkTexture *backingTexture = nullptr;
VkFormat vkformat;
int lastActiveFrameSlot = -1;
friend class QRhiVulkan;
};
struct QVkTexture : public QRhiTexture
{
QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
int sampleCount, Flags flags);
~QVkTexture();
void release() override;
bool build() override;
bool buildFrom(const QRhiNativeHandles *src) override;
const QRhiNativeHandles *nativeHandles() override;
bool prepareBuild(QSize *adjustedSize = nullptr);
bool finishBuild();
VkImage image = VK_NULL_HANDLE;
VkImageView imageView = VK_NULL_HANDLE;
QVkAlloc imageAlloc = nullptr;
VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
bool owns = true;
QRhiVulkanTextureNativeHandles nativeHandlesStruct;
struct UsageState {
// no tracking of subresource layouts (some operations can keep
// subresources in different layouts for some time, but that does not
// need to be kept track of)
VkImageLayout layout;
VkAccessFlags access;
VkPipelineStageFlags stage;
};
UsageState usageState;
VkFormat vkformat;
uint mipLevelCount = 0;
VkSampleCountFlagBits samples;
int lastActiveFrameSlot = -1;
uint generation = 0;
friend class QRhiVulkan;
};
struct QVkSampler : public QRhiSampler
{
QVkSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
AddressMode u, AddressMode v);
~QVkSampler();
void release() override;
bool build() override;
VkSampler sampler = VK_NULL_HANDLE;
int lastActiveFrameSlot = -1;
uint generation = 0;
friend class QRhiVulkan;
};
struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
{
QVkRenderPassDescriptor(QRhiImplementation *rhi);
~QVkRenderPassDescriptor();
void release() override;
VkRenderPass rp = VK_NULL_HANDLE;
bool ownsRp = false;
int lastActiveFrameSlot = -1;
};
struct QVkRenderTargetData
{
VkFramebuffer fb = VK_NULL_HANDLE;
QVkRenderPassDescriptor *rp = nullptr;
QSize pixelSize;
float dpr = 1;
int sampleCount = 1;
int colorAttCount = 0;
int dsAttCount = 0;
int resolveAttCount = 0;
static const int MAX_COLOR_ATTACHMENTS = 8;
};
struct QVkReferenceRenderTarget : public QRhiRenderTarget
{
QVkReferenceRenderTarget(QRhiImplementation *rhi);
~QVkReferenceRenderTarget();
void release() override;
QSize pixelSize() const override;
float devicePixelRatio() const override;
int sampleCount() const override;
QVkRenderTargetData d;
};
struct QVkTextureRenderTarget : public QRhiTextureRenderTarget
{
QVkTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags);
~QVkTextureRenderTarget();
void release() override;
QSize pixelSize() const override;
float devicePixelRatio() const override;
int sampleCount() const override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
bool build() override;
QVkRenderTargetData d;
VkImageView rtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
int lastActiveFrameSlot = -1;
friend class QRhiVulkan;
};
struct QVkShaderResourceBindings : public QRhiShaderResourceBindings
{
QVkShaderResourceBindings(QRhiImplementation *rhi);
~QVkShaderResourceBindings();
void release() override;
bool build() override;
QVector<QRhiShaderResourceBinding> sortedBindings;
int poolIndex = -1;
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
VkDescriptorSet descSets[QVK_FRAMES_IN_FLIGHT]; // multiple sets to support dynamic buffers
int lastActiveFrameSlot = -1;
uint generation = 0;
// Keep track of the generation number of each referenced QRhi* to be able
// to detect that the underlying descriptor set became out of date and they
// need to be written again with the up-to-date VkBuffer etc. objects.
struct BoundUniformBufferData {
quint64 id;
uint generation;
};
struct BoundSampledTextureData {
quint64 texId;
uint texGeneration;
quint64 samplerId;
uint samplerGeneration;
};
struct BoundResourceData {
union {
BoundUniformBufferData ubuf;
BoundSampledTextureData stex;
};
};
QVector<BoundResourceData> boundResourceData[QVK_FRAMES_IN_FLIGHT];
friend class QRhiVulkan;
};
Q_DECLARE_TYPEINFO(QVkShaderResourceBindings::BoundResourceData, Q_MOVABLE_TYPE);
struct QVkGraphicsPipeline : public QRhiGraphicsPipeline
{
QVkGraphicsPipeline(QRhiImplementation *rhi);
~QVkGraphicsPipeline();
void release() override;
bool build() override;
VkPipelineLayout layout = VK_NULL_HANDLE;
VkPipeline pipeline = VK_NULL_HANDLE;
int lastActiveFrameSlot = -1;
uint generation = 0;
friend class QRhiVulkan;
};
struct QVkCommandBuffer : public QRhiCommandBuffer
{
QVkCommandBuffer(QRhiImplementation *rhi);
~QVkCommandBuffer();
void release() override;
VkCommandBuffer cb = VK_NULL_HANDLE;
QRhiVulkanCommandBufferNativeHandles nativeHandlesStruct;
const QRhiNativeHandles *nativeHandles() {
nativeHandlesStruct.commandBuffer = cb;
return &nativeHandlesStruct;
}
void resetState() {
resetCommands();
currentTarget = nullptr;
resetCachedState();
}
void resetCachedState() {
currentPipeline = nullptr;
currentPipelineGeneration = 0;
currentSrb = nullptr;
currentSrbGeneration = 0;
currentDescSetSlot = -1;
currentIndexBuffer = VK_NULL_HANDLE;
currentIndexOffset = 0;
currentIndexFormat = VK_INDEX_TYPE_UINT16;
memset(currentVertexBuffers, 0, sizeof(currentVertexBuffers));
memset(currentVertexOffsets, 0, sizeof(currentVertexOffsets));
}
QRhiRenderTarget *currentTarget;
QRhiGraphicsPipeline *currentPipeline;
uint currentPipelineGeneration;
QRhiShaderResourceBindings *currentSrb;
uint currentSrbGeneration;
int currentDescSetSlot;
VkBuffer currentIndexBuffer;
quint32 currentIndexOffset;
VkIndexType currentIndexFormat;
static const int VERTEX_INPUT_RESOURCE_SLOT_COUNT = 32;
VkBuffer currentVertexBuffers[VERTEX_INPUT_RESOURCE_SLOT_COUNT];
quint32 currentVertexOffsets[VERTEX_INPUT_RESOURCE_SLOT_COUNT];
struct Command {
enum Cmd {
CopyBuffer,
CopyBufferToImage,
CopyImage,
CopyImageToBuffer,
ImageBarrier,
BufferBarrier,
BlitImage,
BeginRenderPass,
EndRenderPass,
BindPipeline,
BindDescriptorSet,
BindVertexBuffer,
BindIndexBuffer,
SetViewport,
SetScissor,
SetBlendConstants,
SetStencilRef,
Draw,
DrawIndexed,
DebugMarkerBegin,
DebugMarkerEnd,
DebugMarkerInsert,
TransitionPassResources
};
Cmd cmd;
union Args {
struct {
VkBuffer src;
VkBuffer dst;
VkBufferCopy desc;
} copyBuffer;
struct {
VkBuffer src;
VkImage dst;
VkImageLayout dstLayout;
int count;
int bufferImageCopyIndex;
} copyBufferToImage;
struct {
VkImage src;
VkImageLayout srcLayout;
VkImage dst;
VkImageLayout dstLayout;
VkImageCopy desc;
} copyImage;
struct {
VkImage src;
VkImageLayout srcLayout;
VkBuffer dst;
VkBufferImageCopy desc;
} copyImageToBuffer;
struct {
VkPipelineStageFlags srcStageMask;
VkPipelineStageFlags dstStageMask;
VkImageMemoryBarrier desc;
} imageBarrier;
struct {
VkPipelineStageFlags srcStageMask;
VkPipelineStageFlags dstStageMask;
VkBufferMemoryBarrier desc;
} bufferBarrier;
struct {
VkImage src;
VkImageLayout srcLayout;
VkImage dst;
VkImageLayout dstLayout;
VkFilter filter;
VkImageBlit desc;
} blitImage;
struct {
VkRenderPassBeginInfo desc;
int clearValueIndex;
} beginRenderPass;
struct {
} endRenderPass;
struct {
VkPipelineBindPoint bindPoint;
VkPipeline pipeline;
} bindPipeline;
struct {
VkPipelineBindPoint bindPoint;
VkPipelineLayout pipelineLayout;
VkDescriptorSet descSet;
int dynamicOffsetCount;
int dynamicOffsetIndex;
} bindDescriptorSet;
struct {
int startBinding;
int count;
int vertexBufferIndex;
int vertexBufferOffsetIndex;
} bindVertexBuffer;
struct {
VkBuffer buf;
VkDeviceSize ofs;
VkIndexType type;
} bindIndexBuffer;
struct {
VkViewport viewport;
} setViewport;
struct {
VkRect2D scissor;
} setScissor;
struct {
float c[4];
} setBlendConstants;
struct {
uint32_t ref;
} setStencilRef;
struct {
uint32_t vertexCount;
uint32_t instanceCount;
uint32_t firstVertex;
uint32_t firstInstance;
} draw;
struct {
uint32_t indexCount;
uint32_t instanceCount;
uint32_t firstIndex;
int32_t vertexOffset;
uint32_t firstInstance;
} drawIndexed;
struct {
VkDebugMarkerMarkerInfoEXT marker;
int markerNameIndex;
} debugMarkerBegin;
struct {
} debugMarkerEnd;
struct {
VkDebugMarkerMarkerInfoEXT marker;
} debugMarkerInsert;
struct {
int trackerIndex;
} transitionResources;
} args;
};
QVector<Command> commands;
QVarLengthArray<QRhiPassResourceTracker, 8> passResTrackers;
int currentPassResTrackerIndex;
void resetCommands() {
commands.clear();
passResTrackers.clear();
currentPassResTrackerIndex = -1;
resetPools();
}
void resetPools() {
pools.clearValue.clear();
pools.bufferImageCopy.clear();
pools.dynamicOffset.clear();
pools.vertexBuffer.clear();
pools.vertexBufferOffset.clear();
pools.debugMarkerName.clear();
}
struct {
QVarLengthArray<VkClearValue, 4> clearValue;
QVarLengthArray<VkBufferImageCopy, 16> bufferImageCopy;
QVarLengthArray<uint32_t, 4> dynamicOffset;
QVarLengthArray<VkBuffer, 4> vertexBuffer;
QVarLengthArray<VkDeviceSize, 4> vertexBufferOffset;
QVarLengthArray<QByteArray, 4> debugMarkerName;
} pools;
friend class QRhiVulkan;
};
Q_DECLARE_TYPEINFO(QVkCommandBuffer::Command, Q_MOVABLE_TYPE);
struct QVkSwapChain : public QRhiSwapChain
{
QVkSwapChain(QRhiImplementation *rhi);
~QVkSwapChain();
void release() override;
QRhiCommandBuffer *currentFrameCommandBuffer() override;
QRhiRenderTarget *currentFrameRenderTarget() override;
QSize surfacePixelSize() override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
bool buildOrResize() override;
bool ensureSurface();
static const quint32 MAX_BUFFER_COUNT = 3;
QWindow *window = nullptr;
QSize pixelSize;
bool supportsReadback = false;
VkSwapchainKHR sc = VK_NULL_HANDLE;
int bufferCount = 0;
VkSurfaceKHR surface = VK_NULL_HANDLE;
VkSurfaceKHR lastConnectedSurface = VK_NULL_HANDLE;
VkFormat colorFormat = VK_FORMAT_B8G8R8A8_UNORM;
VkColorSpaceKHR colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
QVkRenderBuffer *ds = nullptr;
VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
QVector<VkPresentModeKHR> supportedPresentationModes;
VkDeviceMemory msaaImageMem = VK_NULL_HANDLE;
QVkReferenceRenderTarget rtWrapper;
QVkCommandBuffer cbWrapper;
struct ImageResources {
VkImage image = VK_NULL_HANDLE;
VkImageView imageView = VK_NULL_HANDLE;
VkFramebuffer fb = VK_NULL_HANDLE;
VkImage msaaImage = VK_NULL_HANDLE;
VkImageView msaaImageView = VK_NULL_HANDLE;
bool transferSource = false;
} imageRes[MAX_BUFFER_COUNT];
struct FrameResources {
VkFence imageFence = VK_NULL_HANDLE;
bool imageFenceWaitable = false;
VkSemaphore imageSem = VK_NULL_HANDLE;
VkSemaphore drawSem = VK_NULL_HANDLE;
bool imageAcquired = false;
bool imageSemWaitable = false;
quint32 imageIndex = 0;
VkCommandBuffer cmdBuf = VK_NULL_HANDLE;
VkFence cmdFence = VK_NULL_HANDLE;
bool cmdFenceWaitable = false;
int timestampQueryIndex = -1;
} frameRes[QVK_FRAMES_IN_FLIGHT];
quint32 currentImageIndex = 0; // index in imageRes
quint32 currentFrameSlot = 0; // index in frameRes
int frameCount = 0;
friend class QRhiVulkan;
};
class QRhiVulkan : public QRhiImplementation
{
public:
QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *importDevice = nullptr);
bool create(QRhi::Flags flags) override;
void destroy() override;
QRhiGraphicsPipeline *createGraphicsPipeline() override;
QRhiShaderResourceBindings *createShaderResourceBindings() override;
QRhiBuffer *createBuffer(QRhiBuffer::Type type,
QRhiBuffer::UsageFlags usage,
int size) override;
QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
const QSize &pixelSize,
int sampleCount,
QRhiRenderBuffer::Flags flags) override;
QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize,
int sampleCount,
QRhiTexture::Flags flags) override;
QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
QRhiSampler::Filter mipmapMode,
QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override;
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
QRhiTextureRenderTarget::Flags flags) override;
QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
QRhi::FrameOpResult endOffscreenFrame() override;
QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
void beginPass(QRhiCommandBuffer *cb,
QRhiRenderTarget *rt,
const QColor &colorClearValue,
const QRhiDepthStencilClearValue &depthStencilClearValue,
QRhiResourceUpdateBatch *resourceUpdates) override;
void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
void setGraphicsPipeline(QRhiCommandBuffer *cb,
QRhiGraphicsPipeline *ps) override;
void setShaderResources(QRhiCommandBuffer *cb,
QRhiShaderResourceBindings *srb,
int dynamicOffsetCount,
const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override;
void setVertexInput(QRhiCommandBuffer *cb,
int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
QRhiBuffer *indexBuf, quint32 indexOffset,
QRhiCommandBuffer::IndexFormat indexFormat) override;
void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override;
void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
quint32 instanceCount, quint32 firstIndex,
qint32 vertexOffset, quint32 firstInstance) override;
void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override;
void debugMarkEnd(QRhiCommandBuffer *cb) override;
void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override;
const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override;
void beginExternal(QRhiCommandBuffer *cb) override;
void endExternal(QRhiCommandBuffer *cb) override;
QVector<int> supportedSampleCounts() const override;
int ubufAlignment() const override;
bool isYUpInFramebuffer() const override;
bool isYUpInNDC() const override;
bool isClipDepthZeroToOne() const override;
QMatrix4x4 clipSpaceCorrMatrix() const override;
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override;
bool isFeatureSupported(QRhi::Feature feature) const override;
int resourceLimit(QRhi::ResourceLimit limit) const override;
const QRhiNativeHandles *nativeHandles() override;
void sendVMemStatsToProfiler() override;
VkResult createDescriptorPool(VkDescriptorPool *pool);
bool allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex);
uint32_t chooseTransientImageMemType(VkImage img, uint32_t startIndex);
bool createTransientImage(VkFormat format, const QSize &pixelSize, VkImageUsageFlags usage,
VkImageAspectFlags aspectMask, VkSampleCountFlagBits samples,
VkDeviceMemory *mem, VkImage *images, VkImageView *views, int count);
bool recreateSwapChain(QRhiSwapChain *swapChain);
void releaseSwapChainResources(QRhiSwapChain *swapChain);
VkFormat optimalDepthStencilFormat();
VkSampleCountFlagBits effectiveSampleCount(int sampleCount);
bool createDefaultRenderPass(VkRenderPass *rp,
bool hasDepthStencil,
VkSampleCountFlagBits samples,
VkFormat colorFormat);
bool createOffscreenRenderPass(VkRenderPass *rp,
const QVector<QRhiColorAttachment> &colorAttachments,
bool preserveColor,
bool preserveDs,
QRhiRenderBuffer *depthStencilBuffer,
QRhiTexture *depthTexture);
bool ensurePipelineCache();
VkShaderModule createShader(const QByteArray &spirv);
void prepareNewFrame(QRhiCommandBuffer *cb);
QRhi::FrameOpResult startCommandBuffer(VkCommandBuffer *cb);
QRhi::FrameOpResult endAndSubmitCommandBuffer(VkCommandBuffer cb, VkFence cmdFence,
VkSemaphore *waitSem, VkSemaphore *signalSem);
void waitCommandCompletion(int frameSlot);
VkDeviceSize subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const;
using BufferImageCopyList = QVarLengthArray<VkBufferImageCopy, 16>;
void prepareUploadSubres(QVkTexture *texD, int layer, int level,
const QRhiTextureSubresourceUploadDescription &subresDesc,
size_t *curOfs, void *mp,
BufferImageCopyList *copyInfos);
void enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates);
void executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD);
void enqueueTransitionPassResources(QVkCommandBuffer *cbD);
void recordCommandBuffer(QVkCommandBuffer *cbD);
void trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker,
QVkBuffer *bufD,
int slot,
QRhiPassResourceTracker::BufferAccess access,
QRhiPassResourceTracker::BufferStage stage);
void trackedRegisterTexture(QRhiPassResourceTracker *passResTracker,
QVkTexture *texD,
QRhiPassResourceTracker::TextureAccess access,
QRhiPassResourceTracker::TextureStage stage);
void recordTransitionPassResources(QVkCommandBuffer *cbD, const QRhiPassResourceTracker &tracker);
void activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRenderTarget *rtD);
void executeDeferredReleases(bool forced = false);
void finishActiveReadbacks(bool forced = false);
void setObjectName(uint64_t object, VkDebugReportObjectTypeEXT type, const QByteArray &name, int slot = -1);
void trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, int slot,
VkAccessFlags access, VkPipelineStageFlags stage);
void trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD,
VkImageLayout layout, VkAccessFlags access, VkPipelineStageFlags stage);
void subresourceBarrier(QVkCommandBuffer *cbD, VkImage image,
VkImageLayout oldLayout, VkImageLayout newLayout,
VkAccessFlags srcAccess, VkAccessFlags dstAccess,
VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage,
int startLayer, int layerCount,
int startLevel, int levelCount);
void updateShaderResourceBindings(QRhiShaderResourceBindings *srb, int descSetIdx = -1);
QVulkanInstance *inst = nullptr;
QWindow *maybeWindow = nullptr;
bool importedDevice = false;
VkPhysicalDevice physDev = VK_NULL_HANDLE;
VkDevice dev = VK_NULL_HANDLE;
bool importedCmdPool = false;
VkCommandPool cmdPool = VK_NULL_HANDLE;
int gfxQueueFamilyIdx = -1;
VkQueue gfxQueue = VK_NULL_HANDLE;
quint32 timestampValidBits = 0;
bool importedAllocator = false;
QVkAllocator allocator = nullptr;
QVulkanFunctions *f = nullptr;
QVulkanDeviceFunctions *df = nullptr;
VkPhysicalDeviceProperties physDevProperties;
VkDeviceSize ubufAlign;
VkDeviceSize texbufAlign;
bool debugMarkersAvailable = false;
bool vertexAttribDivisorAvailable = false;
PFN_vkCmdDebugMarkerBeginEXT vkCmdDebugMarkerBegin = nullptr;
PFN_vkCmdDebugMarkerEndEXT vkCmdDebugMarkerEnd = nullptr;
PFN_vkCmdDebugMarkerInsertEXT vkCmdDebugMarkerInsert = nullptr;
PFN_vkDebugMarkerSetObjectNameEXT vkDebugMarkerSetObjectName = nullptr;
PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR = nullptr;
PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR;
PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR;
PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR;
PFN_vkQueuePresentKHR vkQueuePresentKHR;
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr;
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
VkPipelineCache pipelineCache = VK_NULL_HANDLE;
struct DescriptorPoolData {
DescriptorPoolData() { }
DescriptorPoolData(VkDescriptorPool pool_)
: pool(pool_)
{ }
VkDescriptorPool pool = VK_NULL_HANDLE;
int refCount = 0;
int allocedDescSets = 0;
};
QVector<DescriptorPoolData> descriptorPools;
VkQueryPool timestampQueryPool = VK_NULL_HANDLE;
QBitArray timestampQueryPoolMap;
VkFormat optimalDsFormat = VK_FORMAT_UNDEFINED;
QMatrix4x4 clipCorrectMatrix;
bool inFrame = false;
bool inPass = false;
QVkSwapChain *currentSwapChain = nullptr;
QSet<QVkSwapChain *> swapchains;
QRhiVulkanNativeHandles nativeHandlesStruct;
struct OffscreenFrame {
OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { }
bool active = false;
QVkCommandBuffer cbWrapper;
VkFence cmdFence = VK_NULL_HANDLE;
} ofr;
struct ActiveReadback {
int activeFrameSlot = -1;
QRhiReadbackDescription desc;
QRhiReadbackResult *result;
VkBuffer buf;
QVkAlloc bufAlloc;
quint32 bufSize;
QSize pixelSize;
QRhiTexture::Format format;
};
QVector<ActiveReadback> activeReadbacks;
struct DeferredReleaseEntry {
enum Type {
Pipeline,
ShaderResourceBindings,
Buffer,
RenderBuffer,
Texture,
Sampler,
TextureRenderTarget,
RenderPass,
StagingBuffer
};
Type type;
int lastActiveFrameSlot; // -1 if not used otherwise 0..FRAMES_IN_FLIGHT-1
union {
struct {
VkPipeline pipeline;
VkPipelineLayout layout;
} pipelineState;
struct {
int poolIndex;
VkDescriptorSetLayout layout;
} shaderResourceBindings;
struct {
VkBuffer buffers[QVK_FRAMES_IN_FLIGHT];
QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT];
VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
} buffer;
struct {
VkDeviceMemory memory;
VkImage image;
VkImageView imageView;
} renderBuffer;
struct {
VkImage image;
VkImageView imageView;
QVkAlloc allocation;
VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
} texture;
struct {
VkSampler sampler;
} sampler;
struct {
VkFramebuffer fb;
VkImageView rtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
} textureRenderTarget;
struct {
VkRenderPass rp;
} renderPass;
struct {
VkBuffer stagingBuffer;
QVkAlloc stagingAllocation;
} stagingBuffer;
};
};
QVector<DeferredReleaseEntry> releaseQueue;
};
Q_DECLARE_TYPEINFO(QRhiVulkan::DescriptorPoolData, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QRhiVulkan::DeferredReleaseEntry, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QRhiVulkan::ActiveReadback, Q_MOVABLE_TYPE);
QT_END_NAMESPACE
#endif

View File

@ -0,0 +1,81 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt RHI module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QRHIVULKANEXT_P_H
#define QRHIVULKANEXT_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qrhivulkan_p.h"
QT_BEGIN_NAMESPACE
#ifndef VK_EXT_vertex_attribute_divisor
#define VK_EXT_vertex_attribute_divisor 1
#define VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_SPEC_VERSION 2
#define VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME "VK_EXT_vertex_attribute_divisor"
typedef struct VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT {
VkStructureType sType;
void* pNext;
uint32_t maxVertexAttribDivisor;
} VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT;
typedef struct VkVertexInputBindingDivisorDescriptionEXT {
uint32_t binding;
uint32_t divisor;
} VkVertexInputBindingDivisorDescriptionEXT;
typedef struct VkPipelineVertexInputDivisorStateCreateInfoEXT {
VkStructureType sType;
const void* pNext;
uint32_t vertexBindingDivisorCount;
const VkVertexInputBindingDivisorDescriptionEXT* pVertexBindingDivisors;
} VkPipelineVertexInputDivisorStateCreateInfoEXT;
#endif // VK_EXT_vertex_attribute_divisor
QT_END_NAMESPACE
#endif

586
src/gui/rhi/qshader.cpp Normal file
View File

@ -0,0 +1,586 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qshader_p_p.h"
#include <QDataStream>
#include <QBuffer>
QT_BEGIN_NAMESPACE
/*!
\class QShader
\inmodule QtRhi
\since 5.14
\brief Contains multiple versions of a shader translated to multiple shading languages,
together with reflection metadata.
QShader is the entry point to shader code in the graphics API agnostic
Qt world. Instead of using GLSL shader sources, as was the custom with Qt
5.x, new graphics systems with backends for multiple graphics APIs, such
as, Vulkan, Metal, Direct3D, and OpenGL, take QShader as their input
whenever a shader needs to be specified.
A QShader instance is empty and thus invalid by default. To get a useful
instance, the two typical methods are:
\list
\li Generate the contents offline, during build time or earlier, using the
\c qsb command line tool. The result is a binary file that is shipped with
the application, read via QIODevice::readAll(), and then deserialized via
fromSerialized(). For more information, see QShaderBaker.
\li Generate at run time via QShaderBaker. This is an expensive operation,
but allows applications to use user-provided or dynamically generated
shader source strings.
\endlist
When used together with the Qt Rendering Hardware Interface and its
classes, like QRhiGraphicsPipeline, no further action is needed from the
application's side as these classes are prepared to consume a QShader
whenever a shader needs to be specified for a given stage of the graphics
pipeline.
Alternatively, applications can access
\list
\li the source or byte code for any of the shading language versions that
are included in the QShader,
\li the name of the entry point for the shader,
\li the reflection metadata containing a description of the shader's
inputs, outputs and resources like uniform blocks. This is essential when
an application or framework needs to discover the inputs of a shader at
runtime due to not having advance knowledge of the vertex attributes or the
layout of the uniform buffers used by the shader.
\endlist
QShader makes no assumption about the shading language that was used
as the source for generating the various versions and variants that are
included in it.
QShader uses implicit sharing similarly to many core Qt types, and so
can be returned or passed by value. Detach happens implicitly when calling
a setter.
For reference, QRhi expects that a QShader suitable for all its
backends contains at least the following:
\list
\li SPIR-V 1.0 bytecode suitable for Vulkan 1.0 or newer
\li GLSL/ES 100 source code suitable for OpenGL ES 2.0 or newer
\li GLSL 120 source code suitable for OpenGL 2.1
\li HLSL Shader Model 5.0 source code or the corresponding DXBC bytecode suitable for Direct3D 11
\li Metal Shading Language 1.2 source code or the corresponding bytecode suitable for Metal
\endlist
\sa QShaderBaker
*/
/*!
\enum QShader::Stage
Describes the stage of the graphics pipeline the shader is suitable for.
\value VertexStage Vertex shader
\value TessellationControlStage Tessellation control (hull) shader
\value TessellationEvaluationStage Tessellation evaluation (domain) shader
\value GeometryStage Geometry shader
\value FragmentStage Fragment (pixel) shader
\value ComputeStage Compute shader
*/
/*!
\class QShaderVersion
\inmodule QtRhi
\brief Specifies the shading language version.
While languages like SPIR-V or the Metal Shading Language use traditional
version numbers, shaders for other APIs can use slightly different
versioning schemes. All those are mapped to a single version number in
here, however. For HLSL, the version refers to the Shader Model version,
like 5.0, 5.1, or 6.0. For GLSL an additional flag is needed to choose
between GLSL and GLSL/ES.
Below is a list with the most common examples of shader versions for
different graphics APIs:
\list
\li Vulkan (SPIR-V): 100
\li OpenGL: 120, 330, 440, etc.
\li OpenGL ES: 100 with GlslEs, 300 with GlslEs, etc.
\li Direct3D: 50, 51, 60
\li Metal: 12, 20
\endlist
A default constructed QShaderVersion contains a version of 100 and no
flags set.
*/
/*!
\enum QShaderVersion::Flag
Describes the flags that can be set.
\value GlslEs Indicates that GLSL/ES is meant in combination with GlslShader
*/
/*!
\class QShaderKey
\inmodule QtRhi
\brief Specifies the shading language, the version with flags, and the variant.
A default constructed QShaderKey has source set to SpirvShader and
sourceVersion set to 100. sourceVariant defaults to StandardShader.
*/
/*!
\enum QShader::Source
Describes what kind of shader code an entry contains.
\value SpirvShader SPIR-V
\value GlslShader GLSL
\value HlslShader HLSL
\value DxbcShader Direct3D bytecode (HLSL compiled by \c fxc)
\value MslShader Metal Shading Language
\value DxilShader Direct3D bytecode (HLSL compiled by \c dxc)
\value MetalLibShader Pre-compiled Metal bytecode
*/
/*!
\enum QShader::Variant
Describes what kind of shader code an entry contains.
\value StandardShader The normal, unmodified version of the shader code.
\value BatchableVertexShader Vertex shader rewritten to be suitable for Qt Quick scenegraph batching.
*/
/*!
\class QShaderCode
\inmodule QtRhi
\brief Contains source or binary code for a shader and additional metadata.
When shader() is empty after retrieving a QShaderCode instance from
QShader, it indicates no shader code was found for the requested key.
*/
static const int QSB_VERSION = 1;
/*!
Constructs a new, empty (and thus invalid) QShader instance.
*/
QShader::QShader()
: d(new QShaderPrivate)
{
}
/*!
\internal
*/
void QShader::detach()
{
qAtomicDetach(d);
}
/*!
\internal
*/
QShader::QShader(const QShader &other)
: d(other.d)
{
d->ref.ref();
}
/*!
\internal
*/
QShader &QShader::operator=(const QShader &other)
{
qAtomicAssign(d, other.d);
return *this;
}
/*!
Destructor.
*/
QShader::~QShader()
{
if (!d->ref.deref())
delete d;
}
/*!
\return true if the QShader contains at least one shader version.
*/
bool QShader::isValid() const
{
return !d->shaders.isEmpty();
}
/*!
\return the pipeline stage the shader is meant for.
*/
QShader::Stage QShader::stage() const
{
return d->stage;
}
/*!
Sets the pipeline \a stage.
*/
void QShader::setStage(Stage stage)
{
if (stage != d->stage) {
detach();
d->stage = stage;
}
}
/*!
\return the reflection metadata for the shader.
*/
QShaderDescription QShader::description() const
{
return d->desc;
}
/*!
Sets the reflection metadata to \a desc.
*/
void QShader::setDescription(const QShaderDescription &desc)
{
detach();
d->desc = desc;
}
/*!
\return the list of available shader versions
*/
QVector<QShaderKey> QShader::availableShaders() const
{
return d->shaders.keys().toVector();
}
/*!
\return the source or binary code for a given shader version specified by \a key.
*/
QShaderCode QShader::shader(const QShaderKey &key) const
{
return d->shaders.value(key);
}
/*!
Stores the source or binary \a shader code for a given shader version specified by \a key.
*/
void QShader::setShader(const QShaderKey &key, const QShaderCode &shader)
{
if (d->shaders.value(key) == shader)
return;
detach();
d->shaders[key] = shader;
}
/*!
Removes the source or binary shader code for a given \a key.
Does nothing when not found.
*/
void QShader::removeShader(const QShaderKey &key)
{
auto it = d->shaders.find(key);
if (it == d->shaders.end())
return;
detach();
d->shaders.erase(it);
}
/*!
\return a serialized binary version of all the data held by the
QShader, suitable for writing to files or other I/O devices.
\sa fromSerialized()
*/
QByteArray QShader::serialized() const
{
QBuffer buf;
QDataStream ds(&buf);
ds.setVersion(QDataStream::Qt_5_10);
if (!buf.open(QIODevice::WriteOnly))
return QByteArray();
ds << QSB_VERSION;
ds << d->stage;
ds << d->desc.toBinaryJson();
ds << d->shaders.count();
for (auto it = d->shaders.cbegin(), itEnd = d->shaders.cend(); it != itEnd; ++it) {
const QShaderKey &k(it.key());
ds << k.source();
ds << k.sourceVersion().version();
ds << k.sourceVersion().flags();
ds << k.sourceVariant();
const QShaderCode &shader(d->shaders.value(k));
ds << shader.shader();
ds << shader.entryPoint();
}
return qCompress(buf.buffer());
}
/*!
Creates a new QShader instance from the given \a data.
\sa serialized()
*/
QShader QShader::fromSerialized(const QByteArray &data)
{
QByteArray udata = qUncompress(data);
QBuffer buf(&udata);
QDataStream ds(&buf);
ds.setVersion(QDataStream::Qt_5_10);
if (!buf.open(QIODevice::ReadOnly))
return QShader();
QShader bs;
QShaderPrivate *d = QShaderPrivate::get(&bs);
Q_ASSERT(d->ref.load() == 1); // must be detached
int intVal;
ds >> intVal;
if (intVal != QSB_VERSION)
return QShader();
ds >> intVal;
d->stage = Stage(intVal);
QByteArray descBin;
ds >> descBin;
d->desc = QShaderDescription::fromBinaryJson(descBin);
int count;
ds >> count;
for (int i = 0; i < count; ++i) {
QShaderKey k;
ds >> intVal;
k.setSource(Source(intVal));
QShaderVersion ver;
ds >> intVal;
ver.setVersion(intVal);
ds >> intVal;
ver.setFlags(QShaderVersion::Flags(intVal));
k.setSourceVersion(ver);
ds >> intVal;
k.setSourceVariant(Variant(intVal));
QShaderCode shader;
QByteArray s;
ds >> s;
shader.setShader(s);
ds >> s;
shader.setEntryPoint(s);
d->shaders[k] = shader;
}
return bs;
}
QShaderVersion::QShaderVersion(int v, Flags f)
: m_version(v), m_flags(f)
{
}
QShaderCode::QShaderCode(const QByteArray &code, const QByteArray &entry)
: m_shader(code), m_entryPoint(entry)
{
}
QShaderKey::QShaderKey(QShader::Source s,
const QShaderVersion &sver,
QShader::Variant svar)
: m_source(s),
m_sourceVersion(sver),
m_sourceVariant(svar)
{
}
/*!
Returns \c true if the two QShader objects \a a and \a b are equal,
meaning they are for the same stage with matching sets of shader source or
binary code.
\relates QShader
*/
bool operator==(const QShader &lhs, const QShader &rhs) Q_DECL_NOTHROW
{
return lhs.d->stage == rhs.d->stage
&& lhs.d->shaders == rhs.d->shaders;
// do not bother with desc, if the shader code is the same, the description must match too
}
/*!
\fn bool operator!=(const QShader &lhs, const QShader &rhs)
Returns \c false if the values in the two QShader objects \a a and \a b
are equal; otherwise returns \c true.
\relates QShader
*/
/*!
Returns the hash value for \a s, using \a seed to seed the calculation.
\relates QShader
*/
uint qHash(const QShader &s, uint seed) Q_DECL_NOTHROW
{
uint h = s.stage();
for (auto it = s.d->shaders.constBegin(), itEnd = s.d->shaders.constEnd(); it != itEnd; ++it)
h += qHash(it.key(), seed) + qHash(it.value().shader(), seed);
return h;
}
/*!
Returns \c true if the two QShaderVersion objects \a a and \a b are
equal.
\relates QShaderVersion
*/
bool operator==(const QShaderVersion &lhs, const QShaderVersion &rhs) Q_DECL_NOTHROW
{
return lhs.version() == rhs.version() && lhs.flags() == rhs.flags();
}
/*!
\fn bool operator!=(const QShaderVersion &lhs, const QShaderVersion &rhs)
Returns \c false if the values in the two QShaderVersion objects \a a
and \a b are equal; otherwise returns \c true.
\relates QShaderVersion
*/
/*!
Returns \c true if the two QShaderKey objects \a a and \a b are equal.
\relates QShaderKey
*/
bool operator==(const QShaderKey &lhs, const QShaderKey &rhs) Q_DECL_NOTHROW
{
return lhs.source() == rhs.source() && lhs.sourceVersion() == rhs.sourceVersion()
&& lhs.sourceVariant() == rhs.sourceVariant();
}
/*!
\fn bool operator!=(const QShaderKey &lhs, const QShaderKey &rhs)
Returns \c false if the values in the two QShaderKey objects \a a
and \a b are equal; otherwise returns \c true.
\relates QShaderKey
*/
/*!
Returns the hash value for \a k, using \a seed to seed the calculation.
\relates QShaderKey
*/
uint qHash(const QShaderKey &k, uint seed) Q_DECL_NOTHROW
{
return seed + 10 * k.source() + k.sourceVersion().version() + k.sourceVersion().flags() + k.sourceVariant();
}
/*!
Returns \c true if the two QShaderCode objects \a a and \a b are equal.
\relates QShaderCode
*/
bool operator==(const QShaderCode &lhs, const QShaderCode &rhs) Q_DECL_NOTHROW
{
return lhs.shader() == rhs.shader() && lhs.entryPoint() == rhs.entryPoint();
}
/*!
\fn bool operator!=(const QShaderCode &lhs, const QShaderCode &rhs)
Returns \c false if the values in the two QShaderCode objects \a a
and \a b are equal; otherwise returns \c true.
\relates QShaderCode
*/
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QShader &bs)
{
const QShaderPrivate *d = bs.d;
QDebugStateSaver saver(dbg);
dbg.nospace() << "QShader("
<< "stage=" << d->stage
<< " shaders=" << d->shaders.keys()
<< " desc.isValid=" << d->desc.isValid()
<< ')';
return dbg;
}
QDebug operator<<(QDebug dbg, const QShaderKey &k)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "ShaderKey(" << k.source()
<< " " << k.sourceVersion()
<< " " << k.sourceVariant() << ")";
return dbg;
}
QDebug operator<<(QDebug dbg, const QShaderVersion &v)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "Version(" << v.version() << " " << v.flags() << ")";
return dbg;
}
#endif // QT_NO_DEBUG_STREAM
QT_END_NAMESPACE

227
src/gui/rhi/qshader_p.h Normal file
View File

@ -0,0 +1,227 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QSHADER_P_H
#define QSHADER_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of a number of Qt sources files. This header file may change from
// version to version without notice, or even be removed.
//
// We mean it.
//
#include <QtGui/qtguiglobal.h>
#include <private/qshaderdescription_p.h>
QT_BEGIN_NAMESPACE
struct QShaderPrivate;
class QShaderKey;
class Q_GUI_EXPORT QShaderVersion
{
public:
enum Flag {
GlslEs = 0x01
};
Q_DECLARE_FLAGS(Flags, Flag)
QShaderVersion() = default;
QShaderVersion(int v, Flags f = Flags());
int version() const { return m_version; }
void setVersion(int v) { m_version = v; }
Flags flags() const { return m_flags; }
void setFlags(Flags f) { m_flags = f; }
private:
int m_version = 100;
Flags m_flags;
Q_DECL_UNUSED_MEMBER quint64 m_reserved = 0;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QShaderVersion::Flags)
Q_DECLARE_TYPEINFO(QShaderVersion, Q_MOVABLE_TYPE);
class Q_GUI_EXPORT QShaderCode
{
public:
QShaderCode() = default;
QShaderCode(const QByteArray &code, const QByteArray &entry = QByteArray());
QByteArray shader() const { return m_shader; }
void setShader(const QByteArray &code) { m_shader = code; }
QByteArray entryPoint() const { return m_entryPoint; }
void setEntryPoint(const QByteArray &entry) { m_entryPoint = entry; }
private:
QByteArray m_shader;
QByteArray m_entryPoint;
Q_DECL_UNUSED_MEMBER quint64 m_reserved = 0;
};
Q_DECLARE_TYPEINFO(QShaderCode, Q_MOVABLE_TYPE);
class Q_GUI_EXPORT QShader
{
public:
enum Stage {
VertexStage = 0,
TessellationControlStage,
TessellationEvaluationStage,
GeometryStage,
FragmentStage,
ComputeStage
};
enum Source {
SpirvShader = 0,
GlslShader,
HlslShader,
DxbcShader, // fxc
MslShader,
DxilShader, // dxc
MetalLibShader // xcrun metal + xcrun metallib
};
enum Variant {
StandardShader = 0,
BatchableVertexShader
};
QShader();
QShader(const QShader &other);
QShader &operator=(const QShader &other);
~QShader();
void detach();
bool isValid() const;
Stage stage() const;
void setStage(Stage stage);
QShaderDescription description() const;
void setDescription(const QShaderDescription &desc);
QVector<QShaderKey> availableShaders() const;
QShaderCode shader(const QShaderKey &key) const;
void setShader(const QShaderKey &key, const QShaderCode &shader);
void removeShader(const QShaderKey &key);
QByteArray serialized() const;
static QShader fromSerialized(const QByteArray &data);
private:
QShaderPrivate *d;
friend struct QShaderPrivate;
friend Q_GUI_EXPORT bool operator==(const QShader &, const QShader &) Q_DECL_NOTHROW;
friend Q_GUI_EXPORT uint qHash(const QShader &, uint) Q_DECL_NOTHROW;
#ifndef QT_NO_DEBUG_STREAM
friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QShader &);
#endif
};
class Q_GUI_EXPORT QShaderKey
{
public:
QShaderKey() = default;
QShaderKey(QShader::Source s,
const QShaderVersion &sver,
QShader::Variant svar = QShader::StandardShader);
QShader::Source source() const { return m_source; }
void setSource(QShader::Source s) { m_source = s; }
QShaderVersion sourceVersion() const { return m_sourceVersion; }
void setSourceVersion(const QShaderVersion &sver) { m_sourceVersion = sver; }
QShader::Variant sourceVariant() const { return m_sourceVariant; }
void setSourceVariant(QShader::Variant svar) { m_sourceVariant = svar; }
private:
QShader::Source m_source = QShader::SpirvShader;
QShaderVersion m_sourceVersion;
QShader::Variant m_sourceVariant = QShader::StandardShader;
Q_DECL_UNUSED_MEMBER quint64 m_reserved = 0;
};
Q_DECLARE_TYPEINFO(QShaderKey, Q_MOVABLE_TYPE);
Q_GUI_EXPORT bool operator==(const QShader &lhs, const QShader &rhs) Q_DECL_NOTHROW;
Q_GUI_EXPORT uint qHash(const QShader &s, uint seed = 0) Q_DECL_NOTHROW;
inline bool operator!=(const QShader &lhs, const QShader &rhs) Q_DECL_NOTHROW
{
return !(lhs == rhs);
}
Q_GUI_EXPORT bool operator==(const QShaderVersion &lhs, const QShaderVersion &rhs) Q_DECL_NOTHROW;
Q_GUI_EXPORT bool operator==(const QShaderKey &lhs, const QShaderKey &rhs) Q_DECL_NOTHROW;
Q_GUI_EXPORT bool operator==(const QShaderCode &lhs, const QShaderCode &rhs) Q_DECL_NOTHROW;
inline bool operator!=(const QShaderVersion &lhs, const QShaderVersion &rhs) Q_DECL_NOTHROW
{
return !(lhs == rhs);
}
inline bool operator!=(const QShaderKey &lhs, const QShaderKey &rhs) Q_DECL_NOTHROW
{
return !(lhs == rhs);
}
inline bool operator!=(const QShaderCode &lhs, const QShaderCode &rhs) Q_DECL_NOTHROW
{
return !(lhs == rhs);
}
Q_GUI_EXPORT uint qHash(const QShaderKey &k, uint seed = 0) Q_DECL_NOTHROW;
#ifndef QT_NO_DEBUG_STREAM
Q_GUI_EXPORT QDebug operator<<(QDebug, const QShader &);
Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QShaderKey &k);
Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QShaderVersion &v);
#endif
QT_END_NAMESPACE
#endif

84
src/gui/rhi/qshader_p_p.h Normal file
View File

@ -0,0 +1,84 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QSHADER_P_P_H
#define QSHADER_P_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of a number of Qt sources files. This header file may change from
// version to version without notice, or even be removed.
//
// We mean it.
//
#include "qshader_p.h"
#include <QtCore/QAtomicInt>
#include <QtCore/QHash>
#include <QtCore/QDebug>
QT_BEGIN_NAMESPACE
struct Q_GUI_EXPORT QShaderPrivate
{
QShaderPrivate()
: ref(1)
{
}
QShaderPrivate(const QShaderPrivate *other)
: ref(1),
stage(other->stage),
desc(other->desc),
shaders(other->shaders)
{
}
static QShaderPrivate *get(QShader *s) { return s->d; }
static const QShaderPrivate *get(const QShader *s) { return s->d; }
QAtomicInt ref;
QShader::Stage stage = QShader::VertexStage;
QShaderDescription desc;
QHash<QShaderKey, QShaderCode> shaders;
};
QT_END_NAMESPACE
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,278 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QSHADERDESCRIPTION_H
#define QSHADERDESCRIPTION_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of a number of Qt sources files. This header file may change from
// version to version without notice, or even be removed.
//
// We mean it.
//
#include <QtGui/qtguiglobal.h>
#include <QtCore/QString>
#include <QtCore/QVector>
QT_BEGIN_NAMESPACE
struct QShaderDescriptionPrivate;
class Q_GUI_EXPORT QShaderDescription
{
public:
QShaderDescription();
QShaderDescription(const QShaderDescription &other);
QShaderDescription &operator=(const QShaderDescription &other);
~QShaderDescription();
void detach();
bool isValid() const;
QByteArray toBinaryJson() const;
QByteArray toJson() const;
static QShaderDescription fromBinaryJson(const QByteArray &data);
enum VariableType {
Unknown = 0,
// do not reorder
Float,
Vec2,
Vec3,
Vec4,
Mat2,
Mat2x3,
Mat2x4,
Mat3,
Mat3x2,
Mat3x4,
Mat4,
Mat4x2,
Mat4x3,
Int,
Int2,
Int3,
Int4,
Uint,
Uint2,
Uint3,
Uint4,
Bool,
Bool2,
Bool3,
Bool4,
Double,
Double2,
Double3,
Double4,
DMat2,
DMat2x3,
DMat2x4,
DMat3,
DMat3x2,
DMat3x4,
DMat4,
DMat4x2,
DMat4x3,
Sampler1D,
Sampler2D,
Sampler2DMS,
Sampler3D,
SamplerCube,
Sampler1DArray,
Sampler2DArray,
Sampler2DMSArray,
Sampler3DArray,
SamplerCubeArray,
SamplerRect,
SamplerBuffer,
Image1D,
Image2D,
Image2DMS,
Image3D,
ImageCube,
Image1DArray,
Image2DArray,
Image2DMSArray,
Image3DArray,
ImageCubeArray,
ImageRect,
ImageBuffer,
Struct
};
enum ImageFormat {
// must match SPIR-V's ImageFormat
ImageFormatUnknown = 0,
ImageFormatRgba32f = 1,
ImageFormatRgba16f = 2,
ImageFormatR32f = 3,
ImageFormatRgba8 = 4,
ImageFormatRgba8Snorm = 5,
ImageFormatRg32f = 6,
ImageFormatRg16f = 7,
ImageFormatR11fG11fB10f = 8,
ImageFormatR16f = 9,
ImageFormatRgba16 = 10,
ImageFormatRgb10A2 = 11,
ImageFormatRg16 = 12,
ImageFormatRg8 = 13,
ImageFormatR16 = 14,
ImageFormatR8 = 15,
ImageFormatRgba16Snorm = 16,
ImageFormatRg16Snorm = 17,
ImageFormatRg8Snorm = 18,
ImageFormatR16Snorm = 19,
ImageFormatR8Snorm = 20,
ImageFormatRgba32i = 21,
ImageFormatRgba16i = 22,
ImageFormatRgba8i = 23,
ImageFormatR32i = 24,
ImageFormatRg32i = 25,
ImageFormatRg16i = 26,
ImageFormatRg8i = 27,
ImageFormatR16i = 28,
ImageFormatR8i = 29,
ImageFormatRgba32ui = 30,
ImageFormatRgba16ui = 31,
ImageFormatRgba8ui = 32,
ImageFormatR32ui = 33,
ImageFormatRgb10a2ui = 34,
ImageFormatRg32ui = 35,
ImageFormatRg16ui = 36,
ImageFormatRg8ui = 37,
ImageFormatR16ui = 38,
ImageFormatR8ui = 39
};
enum ImageFlag {
ReadOnlyImage = 1 << 0,
WriteOnlyImage = 1 << 1
};
Q_DECLARE_FLAGS(ImageFlags, ImageFlag)
// Optional data (like decorations) usually default to an otherwise invalid value (-1 or 0). This is intentional.
struct InOutVariable {
QString name;
VariableType type = Unknown;
int location = -1;
int binding = -1;
int descriptorSet = -1;
ImageFormat imageFormat = ImageFormatUnknown;
ImageFlags imageFlags;
};
struct BlockVariable {
QString name;
VariableType type = Unknown;
int offset = 0;
int size = 0;
QVector<int> arrayDims;
int arrayStride = 0;
int matrixStride = 0;
bool matrixIsRowMajor = false;
QVector<BlockVariable> structMembers;
};
struct UniformBlock {
QString blockName;
QString structName; // instanceName
int size = 0;
int binding = -1;
int descriptorSet = -1;
QVector<BlockVariable> members;
};
struct PushConstantBlock {
QString name;
int size = 0;
QVector<BlockVariable> members;
};
struct StorageBlock {
QString blockName;
QString instanceName;
int knownSize = 0;
int binding = -1;
int descriptorSet = -1;
QVector<BlockVariable> members;
};
QVector<InOutVariable> inputVariables() const;
QVector<InOutVariable> outputVariables() const;
QVector<UniformBlock> uniformBlocks() const;
QVector<PushConstantBlock> pushConstantBlocks() const;
QVector<StorageBlock> storageBlocks() const;
QVector<InOutVariable> combinedImageSamplers() const;
QVector<InOutVariable> storageImages() const;
private:
QShaderDescriptionPrivate *d;
friend struct QShaderDescriptionPrivate;
#ifndef QT_NO_DEBUG_STREAM
friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription &);
#endif
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QShaderDescription::ImageFlags)
#ifndef QT_NO_DEBUG_STREAM
Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription &);
Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::InOutVariable &);
Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::BlockVariable &);
Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::UniformBlock &);
Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::PushConstantBlock &);
Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::StorageBlock &);
#endif
QT_END_NAMESPACE
#endif

View File

@ -0,0 +1,95 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QSHADERDESCRIPTION_P_H
#define QSHADERDESCRIPTION_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of a number of Qt sources files. This header file may change from
// version to version without notice, or even be removed.
//
// We mean it.
//
#include "qshaderdescription_p.h"
#include <QtCore/QVector>
#include <QtCore/QAtomicInt>
#include <QtCore/QJsonDocument>
QT_BEGIN_NAMESPACE
struct Q_GUI_EXPORT QShaderDescriptionPrivate
{
QShaderDescriptionPrivate()
: ref(1)
{
}
QShaderDescriptionPrivate(const QShaderDescriptionPrivate *other)
: ref(1),
inVars(other->inVars),
outVars(other->outVars),
uniformBlocks(other->uniformBlocks),
pushConstantBlocks(other->pushConstantBlocks),
storageBlocks(other->storageBlocks),
combinedImageSamplers(other->combinedImageSamplers),
storageImages(other->storageImages)
{
}
static QShaderDescriptionPrivate *get(QShaderDescription *desc) { return desc->d; }
static const QShaderDescriptionPrivate *get(const QShaderDescription *desc) { return desc->d; }
QJsonDocument makeDoc();
void loadDoc(const QJsonDocument &doc);
QAtomicInt ref;
QVector<QShaderDescription::InOutVariable> inVars;
QVector<QShaderDescription::InOutVariable> outVars;
QVector<QShaderDescription::UniformBlock> uniformBlocks;
QVector<QShaderDescription::PushConstantBlock> pushConstantBlocks;
QVector<QShaderDescription::StorageBlock> storageBlocks;
QVector<QShaderDescription::InOutVariable> combinedImageSamplers;
QVector<QShaderDescription::InOutVariable> storageImages;
};
QT_END_NAMESPACE
#endif

57
src/gui/rhi/rhi.pri Normal file
View File

@ -0,0 +1,57 @@
HEADERS += \
rhi/qrhi_p.h \
rhi/qrhi_p_p.h \
rhi/qrhiprofiler_p.h \
rhi/qrhiprofiler_p_p.h \
rhi/qrhinull_p.h \
rhi/qrhinull_p_p.h \
rhi/qshader_p.h \
rhi/qshader_p_p.h \
rhi/qshaderdescription_p.h \
rhi/qshaderdescription_p_p.h
SOURCES += \
rhi/qrhi.cpp \
rhi/qrhiprofiler.cpp \
rhi/qrhinull.cpp \
rhi/qshaderdescription.cpp \
rhi/qshader.cpp
qtConfig(opengl) {
HEADERS += \
rhi/qrhigles2_p.h \
rhi/qrhigles2_p_p.h
SOURCES += \
rhi/qrhigles2.cpp
}
qtConfig(vulkan) {
HEADERS += \
rhi/qrhivulkan_p.h \
rhi/qrhivulkan_p_p.h
SOURCES += \
rhi/qrhivulkan.cpp
}
win32 {
HEADERS += \
rhi/qrhid3d11_p.h \
rhi/qrhid3d11_p_p.h
SOURCES += \
rhi/qrhid3d11.cpp
LIBS += -ld3d11 -ldxgi -ldxguid -ld3dcompiler
}
# darwin {
macos {
HEADERS += \
rhi/qrhimetal_p.h \
rhi/qrhimetal_p_p.h
SOURCES += \
rhi/qrhimetal.mm
LIBS += -framework AppKit -framework Metal
}
include($$PWD/../../3rdparty/VulkanMemoryAllocator.pri)

View File

@ -13,6 +13,7 @@ SUBDIRS = \
text \
util \
itemmodels \
rhi
!qtConfig(opengl)|winrt: SUBDIRS -= qopengl qopenglconfig

View 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);
}

View 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;
}

View File

@ -0,0 +1,8 @@
TARGET = tst_qrhi
CONFIG += testcase
QT += testlib gui-private
SOURCES += tst_qrhi.cpp
RESOURCES += qrhi.qrc

View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>data</file>
</qresource>
</RCC>

View File

@ -0,0 +1,246 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <QThread>
#include <QFile>
#include <QOffscreenSurface>
#include <QtGui/private/qrhi_p.h>
#include <QtGui/private/qrhinull_p.h>
#if QT_CONFIG(opengl)
# include <QtGui/private/qrhigles2_p.h>
# define TST_GL
#endif
#if QT_CONFIG(vulkan)
# include <QVulkanInstance>
# include <QtGui/private/qrhivulkan_p.h>
# define TST_VK
#endif
#ifdef Q_OS_WIN
#include <QtGui/private/qrhid3d11_p.h>
# define TST_D3D11
#endif
#ifdef Q_OS_DARWIN
# include <QtGui/private/qrhimetal_p.h>
# define TST_MTL
#endif
Q_DECLARE_METATYPE(QRhi::Implementation)
Q_DECLARE_METATYPE(QRhiInitParams *)
class tst_QRhi : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void cleanupTestCase();
void create_data();
void create();
private:
struct {
QRhiNullInitParams null;
#ifdef TST_GL
QRhiGles2InitParams gl;
#endif
#ifdef TST_VK
QRhiVulkanInitParams vk;
#endif
#ifdef TST_D3D11
QRhiD3D11InitParams d3d;
#endif
#ifdef TST_MTL
QRhiMetalInitParams mtl;
#endif
} initParams;
#ifdef TST_VK
QVulkanInstance vulkanInstance;
#endif
QOffscreenSurface *fallbackSurface = nullptr;
};
void tst_QRhi::initTestCase()
{
#ifdef TST_GL
fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
initParams.gl.fallbackSurface = fallbackSurface;
#endif
#ifdef TST_VK
vulkanInstance.create();
initParams.vk.inst = &vulkanInstance;
#endif
}
void tst_QRhi::cleanupTestCase()
{
#ifdef TST_VK
vulkanInstance.destroy();
#endif
delete fallbackSurface;
}
void tst_QRhi::create_data()
{
QTest::addColumn<QRhi::Implementation>("impl");
QTest::addColumn<QRhiInitParams *>("initParams");
QTest::newRow("Null") << QRhi::Null << static_cast<QRhiInitParams *>(&initParams.null);
#ifdef TST_GL
QTest::newRow("OpenGL") << QRhi::OpenGLES2 << static_cast<QRhiInitParams *>(&initParams.gl);
#endif
#ifdef TST_VK
if (vulkanInstance.isValid())
QTest::newRow("Vulkan") << QRhi::Vulkan << static_cast<QRhiInitParams *>(&initParams.vk);
#endif
#ifdef TST_D3D11
QTest::newRow("Direct3D 11") << QRhi::D3D11 << static_cast<QRhiInitParams *>(&initParams.d3d);
#endif
#ifdef TST_MTL
QTest::newRow("Metal") << QRhi::Metal << static_cast<QRhiInitParams *>(&initParams.mtl);
#endif
}
static int aligned(int v, int a)
{
return (v + a - 1) & ~(a - 1);
}
void tst_QRhi::create()
{
// Merely attempting to create a QRhi should survive, with an error when
// not supported. (of course, there is always a chance we encounter a crash
// due to some random graphics stack...)
QFETCH(QRhi::Implementation, impl);
QFETCH(QRhiInitParams *, initParams);
QScopedPointer<QRhi> rhi(QRhi::create(impl, initParams, QRhi::Flags(), nullptr));
if (rhi) {
QCOMPARE(rhi->backend(), impl);
QCOMPARE(rhi->thread(), QThread::currentThread());
int cleanupOk = 0;
QRhi *rhiPtr = rhi.data();
auto cleanupFunc = [rhiPtr, &cleanupOk](QRhi *dyingRhi) {
if (rhiPtr == dyingRhi)
cleanupOk += 1;
};
rhi->addCleanupCallback(cleanupFunc);
rhi->runCleanup();
QCOMPARE(cleanupOk, 1);
cleanupOk = 0;
rhi->addCleanupCallback(cleanupFunc);
QRhiResourceUpdateBatch *resUpd = rhi->nextResourceUpdateBatch();
QVERIFY(resUpd);
resUpd->release();
QVERIFY(!rhi->supportedSampleCounts().isEmpty());
QVERIFY(rhi->supportedSampleCounts().contains(1));
QVERIFY(rhi->ubufAlignment() > 0);
QCOMPARE(rhi->ubufAligned(123), aligned(123, rhi->ubufAlignment()));
QCOMPARE(rhi->mipLevelsForSize(QSize(512, 300)), 10);
QCOMPARE(rhi->sizeForMipLevel(0, QSize(512, 300)), QSize(512, 300));
QCOMPARE(rhi->sizeForMipLevel(1, QSize(512, 300)), QSize(256, 150));
QCOMPARE(rhi->sizeForMipLevel(2, QSize(512, 300)), QSize(128, 75));
QCOMPARE(rhi->sizeForMipLevel(9, QSize(512, 300)), QSize(1, 1));
const bool fbUp = rhi->isYUpInFramebuffer();
const bool ndcUp = rhi->isYUpInNDC();
const bool d0to1 = rhi->isClipDepthZeroToOne();
const QMatrix4x4 corrMat = rhi->clipSpaceCorrMatrix();
if (impl == QRhi::OpenGLES2) {
QVERIFY(fbUp);
QVERIFY(ndcUp);
QVERIFY(!d0to1);
QVERIFY(corrMat.isIdentity());
} else if (impl == QRhi::Vulkan) {
QVERIFY(!fbUp);
QVERIFY(!ndcUp);
QVERIFY(d0to1);
QVERIFY(!corrMat.isIdentity());
} else if (impl == QRhi::D3D11) {
QVERIFY(!fbUp);
QVERIFY(ndcUp);
QVERIFY(d0to1);
QVERIFY(!corrMat.isIdentity());
} else if (impl == QRhi::Metal) {
QVERIFY(!fbUp);
QVERIFY(ndcUp);
QVERIFY(d0to1);
QVERIFY(!corrMat.isIdentity());
}
const int texMin = rhi->resourceLimit(QRhi::TextureSizeMin);
const int texMax = rhi->resourceLimit(QRhi::TextureSizeMax);
const int maxAtt = rhi->resourceLimit(QRhi::MaxColorAttachments);
QVERIFY(texMin >= 1);
QVERIFY(texMax >= texMin);
QVERIFY(maxAtt >= 1);
QVERIFY(rhi->nativeHandles());
QVERIFY(rhi->profiler());
const QRhi::Feature features[] = {
QRhi::MultisampleTexture,
QRhi::MultisampleRenderBuffer,
QRhi::DebugMarkers,
QRhi::Timestamps,
QRhi::Instancing,
QRhi::CustomInstanceStepRate,
QRhi::PrimitiveRestart,
QRhi::NonDynamicUniformBuffers,
QRhi::NonFourAlignedEffectiveIndexBufferOffset,
QRhi::NPOTTextureRepeat,
QRhi::RedOrAlpha8IsRed,
QRhi::ElementIndexUint
};
for (size_t i = 0; i <sizeof(features) / sizeof(QRhi::Feature); ++i)
rhi->isFeatureSupported(features[i]);
QVERIFY(rhi->isTextureFormatSupported(QRhiTexture::RGBA8));
rhi.reset();
QCOMPARE(cleanupOk, 1);
}
}
#include <tst_qrhi.moc>
QTEST_MAIN(tst_QRhi)

View File

@ -0,0 +1,18 @@
#version 440
layout(location = 0) in vec4 position;
layout(location = 1) in vec3 color;
layout(location = 0) out vec3 v_color;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
float opacity;
} ubuf;
out gl_PerVertex { vec4 gl_Position; };
void main()
{
v_color = color;
gl_Position = ubuf.mvp * position;
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,8 @@
TARGET = tst_qshader
CONFIG += testcase
QT += testlib gui-private
SOURCES += tst_qshader.cpp
RESOURCES += qshader.qrc

View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>data</file>
</qresource>
</RCC>

View File

@ -0,0 +1,233 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <QFile>
#include <QtGui/private/qshaderdescription_p_p.h>
#include <QtGui/private/qshader_p_p.h>
class tst_QShader : public QObject
{
Q_OBJECT
private slots:
void simpleCompileCheckResults();
void genVariants();
void shaderDescImplicitSharing();
void bakedShaderImplicitSharing();
};
static QShader getShader(const QString &name)
{
QFile f(name);
if (f.open(QIODevice::ReadOnly))
return QShader::fromSerialized(f.readAll());
return QShader();
}
void tst_QShader::simpleCompileCheckResults()
{
QShader s = getShader(QLatin1String(":/data/color_simple.vert.qsb"));
QVERIFY(s.isValid());
QCOMPARE(s.availableShaders().count(), 1);
const QShaderCode shader = s.shader(QShaderKey(QShader::SpirvShader,
QShaderVersion(100)));
QVERIFY(!shader.shader().isEmpty());
QCOMPARE(shader.entryPoint(), QByteArrayLiteral("main"));
const QShaderDescription desc = s.description();
QVERIFY(desc.isValid());
QCOMPARE(desc.inputVariables().count(), 2);
for (const QShaderDescription::InOutVariable &v : desc.inputVariables()) {
switch (v.location) {
case 0:
QCOMPARE(v.name, QLatin1String("position"));
QCOMPARE(v.type, QShaderDescription::Vec4);
break;
case 1:
QCOMPARE(v.name, QLatin1String("color"));
QCOMPARE(v.type, QShaderDescription::Vec3);
break;
default:
QVERIFY(false);
break;
}
}
QCOMPARE(desc.outputVariables().count(), 1);
for (const QShaderDescription::InOutVariable &v : desc.outputVariables()) {
switch (v.location) {
case 0:
QCOMPARE(v.name, QLatin1String("v_color"));
QCOMPARE(v.type, QShaderDescription::Vec3);
break;
default:
QVERIFY(false);
break;
}
}
QCOMPARE(desc.uniformBlocks().count(), 1);
const QShaderDescription::UniformBlock blk = desc.uniformBlocks().first();
QCOMPARE(blk.blockName, QLatin1String("buf"));
QCOMPARE(blk.structName, QLatin1String("ubuf"));
QCOMPARE(blk.size, 68);
QCOMPARE(blk.binding, 0);
QCOMPARE(blk.descriptorSet, 0);
QCOMPARE(blk.members.count(), 2);
for (int i = 0; i < blk.members.count(); ++i) {
const QShaderDescription::BlockVariable v = blk.members[i];
switch (i) {
case 0:
QCOMPARE(v.offset, 0);
QCOMPARE(v.size, 64);
QCOMPARE(v.name, QLatin1String("mvp"));
QCOMPARE(v.type, QShaderDescription::Mat4);
QCOMPARE(v.matrixStride, 16);
break;
case 1:
QCOMPARE(v.offset, 64);
QCOMPARE(v.size, 4);
QCOMPARE(v.name, QLatin1String("opacity"));
QCOMPARE(v.type, QShaderDescription::Float);
break;
default:
QVERIFY(false);
break;
}
}
}
void tst_QShader::genVariants()
{
QShader s = getShader(QLatin1String(":/data/color.vert.qsb"));
// spirv, glsl 100, glsl 330, glsl 120, hlsl 50, msl 12
// + batchable variants
QVERIFY(s.isValid());
QCOMPARE(s.availableShaders().count(), 2 * 6);
int batchableVariantCount = 0;
int batchableGlslVariantCount = 0;
for (const QShaderKey &key : s.availableShaders()) {
if (key.sourceVariant() == QShader::BatchableVertexShader) {
++batchableVariantCount;
if (key.source() == QShader::GlslShader) {
++batchableGlslVariantCount;
const QByteArray src = s.shader(key).shader();
QVERIFY(src.contains(QByteArrayLiteral("_qt_order * ")));
}
}
}
QCOMPARE(batchableVariantCount, 6);
QCOMPARE(batchableGlslVariantCount, 3);
}
void tst_QShader::shaderDescImplicitSharing()
{
QShader s = getShader(QLatin1String(":/data/color_simple.vert.qsb"));
QVERIFY(s.isValid());
QCOMPARE(s.availableShaders().count(), 1);
QVERIFY(s.availableShaders().contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100))));
QShaderDescription d0 = s.description();
QVERIFY(d0.isValid());
QCOMPARE(d0.inputVariables().count(), 2);
QCOMPARE(d0.outputVariables().count(), 1);
QCOMPARE(d0.uniformBlocks().count(), 1);
QShaderDescription d1 = d0;
QVERIFY(QShaderDescriptionPrivate::get(&d0) == QShaderDescriptionPrivate::get(&d1));
QCOMPARE(d0.inputVariables().count(), 2);
QCOMPARE(d0.outputVariables().count(), 1);
QCOMPARE(d0.uniformBlocks().count(), 1);
QCOMPARE(d1.inputVariables().count(), 2);
QCOMPARE(d1.outputVariables().count(), 1);
QCOMPARE(d1.uniformBlocks().count(), 1);
d1.detach();
QVERIFY(QShaderDescriptionPrivate::get(&d0) != QShaderDescriptionPrivate::get(&d1));
QCOMPARE(d0.inputVariables().count(), 2);
QCOMPARE(d0.outputVariables().count(), 1);
QCOMPARE(d0.uniformBlocks().count(), 1);
QCOMPARE(d1.inputVariables().count(), 2);
QCOMPARE(d1.outputVariables().count(), 1);
QCOMPARE(d1.uniformBlocks().count(), 1);
}
void tst_QShader::bakedShaderImplicitSharing()
{
QShader s0 = getShader(QLatin1String(":/data/color_simple.vert.qsb"));
QVERIFY(s0.isValid());
QCOMPARE(s0.availableShaders().count(), 1);
QVERIFY(s0.availableShaders().contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100))));
{
QShader s1 = s0;
QVERIFY(QShaderPrivate::get(&s0) == QShaderPrivate::get(&s1));
QCOMPARE(s0.availableShaders().count(), 1);
QVERIFY(s0.availableShaders().contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100))));
QCOMPARE(s1.availableShaders().count(), 1);
QVERIFY(s1.availableShaders().contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100))));
QCOMPARE(s0.stage(), s1.stage());
QCOMPARE(s0, s1);
s1.detach();
QVERIFY(QShaderPrivate::get(&s0) != QShaderPrivate::get(&s1));
QCOMPARE(s0.availableShaders().count(), 1);
QVERIFY(s0.availableShaders().contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100))));
QCOMPARE(s1.availableShaders().count(), 1);
QVERIFY(s1.availableShaders().contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100))));
QCOMPARE(s0.stage(), s1.stage());
QCOMPARE(s0, s1);
}
{
QShader s1 = s0;
QVERIFY(QShaderPrivate::get(&s0) == QShaderPrivate::get(&s1));
QCOMPARE(s0.stage(), s1.stage());
s1.setStage(QShader::FragmentStage); // call a setter to trigger a detach
QVERIFY(QShaderPrivate::get(&s0) != QShaderPrivate::get(&s1));
QCOMPARE(s0.availableShaders().count(), 1);
QVERIFY(s0.availableShaders().contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100))));
QCOMPARE(s1.availableShaders().count(), 1);
QVERIFY(s1.availableShaders().contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100))));
QShaderDescription d0 = s0.description();
QCOMPARE(d0.inputVariables().count(), 2);
QCOMPARE(d0.outputVariables().count(), 1);
QCOMPARE(d0.uniformBlocks().count(), 1);
QShaderDescription d1 = s1.description();
QCOMPARE(d1.inputVariables().count(), 2);
QCOMPARE(d1.outputVariables().count(), 1);
QCOMPARE(d1.uniformBlocks().count(), 1);
QVERIFY(s0 != s1);
}
}
#include <tst_qshader.moc>
QTEST_MAIN(tst_QShader)

View File

@ -0,0 +1,4 @@
TEMPLATE=subdirs
SUBDIRS= \
qshader \
qrhi

View File

@ -53,7 +53,8 @@ shortcuts \
dialogs \
windowtransparency \
unc \
qtabbar
qtabbar \
rhi
!qtConfig(openssl): SUBDIRS -= qssloptions

View File

@ -0,0 +1,198 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "../shared/examplefw.h"
#include "../shared/cube.h"
#include "../shared/dds_bc1.h"
struct {
QRhiBuffer *vbuf = nullptr;
bool vbufReady = false;
QRhiBuffer *ubuf = nullptr;
QRhiTexture *tex = nullptr;
QRhiSampler *sampler = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
float rotation = 0;
QByteArrayList compressedData;
} d;
void Window::customInit()
{
if (!m_r->isTextureFormatSupported(QRhiTexture::BC1))
qFatal("This backend does not support BC1");
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(cube));
d.vbuf->build();
d.vbufReady = false;
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
d.ubuf->build();
QSize imageSize;
d.compressedData = loadBC1(QLatin1String(":/qt256_bc1_9mips.dds"), &imageSize);
qDebug() << d.compressedData.count() << imageSize << m_r->mipLevelsForSize(imageSize);
d.tex = m_r->newTexture(QRhiTexture::BC1, imageSize, 1, QRhiTexture::MipMapped);
d.tex->build();
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::Linear,
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
d.sampler->build();
d.srb = m_r->newShaderResourceBindings();
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler)
});
d.srb->build();
d.ps = m_r->newGraphicsPipeline();
d.ps->setDepthTest(true);
d.ps->setDepthWrite(true);
d.ps->setDepthOp(QRhiGraphicsPipeline::Less);
d.ps->setCullMode(QRhiGraphicsPipeline::Back);
d.ps->setFrontFace(QRhiGraphicsPipeline::CCW);
const QShader vs = getShader(QLatin1String(":/texture.vert.qsb"));
if (!vs.isValid())
qFatal("Failed to load shader pack (vertex)");
const QShader fs = getShader(QLatin1String(":/texture.frag.qsb"));
if (!fs.isValid())
qFatal("Failed to load shader pack (fragment)");
d.ps->setShaderStages({
{ QRhiGraphicsShaderStage::Vertex, vs },
{ QRhiGraphicsShaderStage::Fragment, fs }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 3 * sizeof(float) },
{ 2 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float3, 0 },
{ 1, 1, QRhiVertexInputAttribute::Float2, 0 }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->build();
}
void Window::customRelease()
{
delete d.ps;
d.ps = nullptr;
delete d.srb;
d.srb = nullptr;
delete d.ubuf;
d.ubuf = nullptr;
delete d.vbuf;
d.vbuf = nullptr;
delete d.sampler;
d.sampler = nullptr;
delete d.tex;
d.tex = nullptr;
}
void Window::customRender()
{
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (!d.vbufReady) {
d.vbufReady = true;
u->uploadStaticBuffer(d.vbuf, cube);
qint32 flip = 0;
u->updateDynamicBuffer(d.ubuf, 64, 4, &flip);
}
if (!d.compressedData.isEmpty()) {
QRhiTextureUploadDescription desc;
for (int i = 0; i < d.compressedData.count(); ++i) {
QRhiTextureSubresourceUploadDescription image(d.compressedData[i].constData(), d.compressedData[i].size());
desc.append({ 0, i, image });
}
u->uploadTexture(d.tex, desc);
d.compressedData.clear();
}
d.rotation += 1.0f;
QMatrix4x4 mvp = m_proj;
mvp.scale(0.5f);
mvp.rotate(d.rotation, 0, 1, 0);
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
const QSize outputSizeInPixels = m_sc->currentPixelSize();
cb->beginPass(m_sc->currentFrameRenderTarget(), QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f), { 1.0f, 0 }, u);
cb->setGraphicsPipeline(d.ps);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBindings[] = {
{ d.vbuf, 0 },
{ d.vbuf, 36 * 3 * sizeof(float) }
};
cb->setVertexInput(0, 2, vbufBindings);
cb->draw(36);
cb->endPass();
}

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
compressedtexture_bc1.cpp
RESOURCES = compressedtexture_bc1.qrc

View File

@ -0,0 +1,7 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="qt256_bc1_9mips.dds">../shared/qt256_bc1_9mips.dds</file>
<file alias="texture.vert.qsb">../shared/texture.vert.qsb</file>
<file alias="texture.frag.qsb">../shared/texture.frag.qsb</file>
</qresource>
</RCC>

View File

@ -0,0 +1,213 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "../shared/examplefw.h"
#include "../shared/cube.h"
#include "../shared/dds_bc1.h"
struct {
QRhiBuffer *vbuf = nullptr;
bool vbufReady = false;
QRhiBuffer *ubuf = nullptr;
QRhiTexture *tex = nullptr;
QRhiSampler *sampler = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
float rotation = 0;
QByteArrayList compressedData;
QByteArrayList compressedData2;
} d;
void Window::customInit()
{
if (!m_r->isTextureFormatSupported(QRhiTexture::BC1))
qFatal("This backend does not support BC1");
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(cube));
d.vbuf->build();
d.vbufReady = false;
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
d.ubuf->build();
QSize imageSize;
d.compressedData = loadBC1(QLatin1String(":/qt256_bc1_9mips.dds"), &imageSize);
Q_ASSERT(imageSize == QSize(256, 256));
d.tex = m_r->newTexture(QRhiTexture::BC1, imageSize);
d.tex->build();
d.compressedData2 = loadBC1(QLatin1String(":/bwqt224_64_nomips.dds"), &imageSize);
Q_ASSERT(imageSize == QSize(224, 64));
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None, // no mipmapping here
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
d.sampler->build();
d.srb = m_r->newShaderResourceBindings();
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler)
});
d.srb->build();
d.ps = m_r->newGraphicsPipeline();
d.ps->setDepthTest(true);
d.ps->setDepthWrite(true);
d.ps->setDepthOp(QRhiGraphicsPipeline::Less);
d.ps->setCullMode(QRhiGraphicsPipeline::Back);
d.ps->setFrontFace(QRhiGraphicsPipeline::CCW);
const QShader vs = getShader(QLatin1String(":/texture.vert.qsb"));
if (!vs.isValid())
qFatal("Failed to load shader pack (vertex)");
const QShader fs = getShader(QLatin1String(":/texture.frag.qsb"));
if (!fs.isValid())
qFatal("Failed to load shader pack (fragment)");
d.ps->setShaderStages({
{ QRhiGraphicsShaderStage::Vertex, vs },
{ QRhiGraphicsShaderStage::Fragment, fs }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 3 * sizeof(float) },
{ 2 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float3, 0 },
{ 1, 1, QRhiVertexInputAttribute::Float2, 0 }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->build();
}
void Window::customRelease()
{
delete d.ps;
d.ps = nullptr;
delete d.srb;
d.srb = nullptr;
delete d.ubuf;
d.ubuf = nullptr;
delete d.vbuf;
d.vbuf = nullptr;
delete d.sampler;
d.sampler = nullptr;
delete d.tex;
d.tex = nullptr;
}
void Window::customRender()
{
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (!d.vbufReady) {
d.vbufReady = true;
u->uploadStaticBuffer(d.vbuf, cube);
qint32 flip = 0;
u->updateDynamicBuffer(d.ubuf, 64, 4, &flip);
}
if (!d.compressedData.isEmpty()) {
{
QRhiTextureUploadDescription desc({ 0, 0, { d.compressedData[0].constData(), d.compressedData[0].size() } });
u->uploadTexture(d.tex, desc);
d.compressedData.clear();
}
// now exercise uploading a smaller compressed image into the same texture
{
QRhiTextureSubresourceUploadDescription image(d.compressedData2[0].constData(), d.compressedData2[0].size());
// positions and sizes must be multiples of 4 here (for BC1)
image.setDestinationTopLeft(QPoint(16, 32));
// the image is smaller than the subresource size (224x64 vs
// 256x256) so the size must be specified manually
image.setSourceSize(QSize(224, 64));
QRhiTextureUploadDescription desc({ 0, 0, image });
u->uploadTexture(d.tex, desc);
d.compressedData2.clear();
}
}
d.rotation += 1.0f;
QMatrix4x4 mvp = m_proj;
mvp.scale(0.5f);
mvp.rotate(d.rotation, 0, 1, 0);
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
const QSize outputSizeInPixels = m_sc->currentPixelSize();
cb->beginPass(m_sc->currentFrameRenderTarget(), QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f), { 1.0f, 0 }, u);
cb->setGraphicsPipeline(d.ps);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBindings[] = {
{ d.vbuf, 0 },
{ d.vbuf, 36 * 3 * sizeof(float) }
};
cb->setVertexInput(0, 2, vbufBindings);
cb->draw(36);
cb->endPass();
}

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
compressedtexture_bc1_subupload.cpp
RESOURCES = compressedtexture_bc1_subupload.qrc

View File

@ -0,0 +1,8 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="qt256_bc1_9mips.dds">../shared/qt256_bc1_9mips.dds</file>
<file alias="bwqt224_64_nomips.dds">../shared/bwqt224_64_nomips.dds</file>
<file alias="texture.vert.qsb">../shared/texture.vert.qsb</file>
<file alias="texture.frag.qsb">../shared/texture.frag.qsb</file>
</qresource>
</RCC>

View File

@ -0,0 +1,2 @@
qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c cubemap.vert -o cubemap.vert.qsb
qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c cubemap.frag -o cubemap.frag.qsb

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -0,0 +1,179 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "../shared/examplefw.h"
#include "../shared/cube.h"
struct {
QVector<QRhiResource *> releasePool;
QRhiBuffer *vbuf = nullptr;
QRhiBuffer *ubuf = nullptr;
QRhiTexture *tex = nullptr;
QRhiSampler *sampler = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
} d;
void Window::customInit()
{
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(cube));
d.vbuf->build();
d.releasePool << d.vbuf;
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64);
d.ubuf->build();
d.releasePool << d.ubuf;
const QSize cubeMapSize(512, 512);
d.tex = m_r->newTexture(QRhiTexture::RGBA8, cubeMapSize, 1, QRhiTexture::CubeMap);
d.releasePool << d.tex;
d.tex->build();
d.initialUpdates = m_r->nextResourceUpdateBatch();
d.initialUpdates->uploadStaticBuffer(d.vbuf, cube);
QImage img = QImage(":/c.png").mirrored().convertToFormat(QImage::Format_RGBA8888);
// just use the same image for all faces for now
QRhiTextureSubresourceUploadDescription subresDesc(img);
QRhiTextureUploadDescription desc({
{ 0, 0, subresDesc }, // +X
{ 1, 0, subresDesc }, // -X
{ 2, 0, subresDesc }, // +Y
{ 3, 0, subresDesc }, // -Y
{ 4, 0, subresDesc }, // +Z
{ 5, 0, subresDesc } // -Z
});
d.initialUpdates->uploadTexture(d.tex, desc);
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
QRhiSampler::Repeat, QRhiSampler::Repeat);
d.releasePool << d.sampler;
d.sampler->build();
d.srb = m_r->newShaderResourceBindings();
d.releasePool << d.srb;
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler)
});
d.srb->build();
d.ps = m_r->newGraphicsPipeline();
d.releasePool << d.ps;
d.ps->setDepthTest(true);
d.ps->setDepthWrite(true);
d.ps->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
d.ps->setCullMode(QRhiGraphicsPipeline::Front); // we are inside the cube so cull front, not back
d.ps->setFrontFace(QRhiGraphicsPipeline::CCW); // front is ccw in the cube data
QShader vs = getShader(QLatin1String(":/cubemap.vert.qsb"));
Q_ASSERT(vs.isValid());
QShader fs = getShader(QLatin1String(":/cubemap.frag.qsb"));
Q_ASSERT(fs.isValid());
d.ps->setShaderStages({
{ QRhiGraphicsShaderStage::Vertex, vs },
{ QRhiGraphicsShaderStage::Fragment, fs }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 3 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float3, 0 }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->build();
}
void Window::customRelease()
{
qDeleteAll(d.releasePool);
d.releasePool.clear();
}
void Window::customRender()
{
const QSize outputSizeInPixels = m_sc->currentPixelSize();
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
}
QMatrix4x4 mvp = m_r->clipSpaceCorrMatrix();
mvp.perspective(90.0f, outputSizeInPixels.width() / (float) outputSizeInPixels.height(), 0.01f, 1000.0f);
// cube vertices go from -1..1
mvp.scale(10);
static float rx = 0;
mvp.rotate(rx, 1, 0, 0);
rx += 0.5f;
// no translation
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
cb->beginPass(m_sc->currentFrameRenderTarget(), QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f), { 1.0f, 0 }, u);
cb->setGraphicsPipeline(d.ps);
cb->setViewport(QRhiViewport(0, 0, outputSizeInPixels.width(), outputSizeInPixels.height()));
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(36);
cb->endPass();
}

View File

@ -0,0 +1,10 @@
#version 440
layout(location = 0) in vec3 v_coord;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform samplerCube tex;
void main()
{
fragColor = vec4(texture(tex, v_coord).rgb, 1.0);
}

Binary file not shown.

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
cubemap.cpp
RESOURCES = cubemap.qrc

View File

@ -0,0 +1,7 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>cubemap.vert.qsb</file>
<file>cubemap.frag.qsb</file>
<file>c.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,15 @@
#version 440
layout(location = 0) in vec4 position;
layout(location = 0) out vec3 v_coord;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
} ubuf;
out gl_PerVertex { vec4 gl_Position; };
void main()
{
v_coord = position.xyz;
gl_Position = ubuf.mvp * position;
}

Binary file not shown.

View File

@ -0,0 +1,328 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "../shared/examplefw.h"
#include <qmath.h>
QByteArray loadHdr(const QString &fn, QSize *size)
{
QFile f(fn);
if (!f.open(QIODevice::ReadOnly)) {
qWarning("Failed to open %s", qPrintable(fn));
return QByteArray();
}
char sig[256];
f.read(sig, 11);
if (strncmp(sig, "#?RADIANCE\n", 11))
return QByteArray();
QByteArray buf = f.readAll();
const char *p = buf.constData();
const char *pEnd = p + buf.size();
// Process lines until the empty one.
QByteArray line;
while (p < pEnd) {
char c = *p++;
if (c == '\n') {
if (line.isEmpty())
break;
if (line.startsWith(QByteArrayLiteral("FORMAT="))) {
const QByteArray format = line.mid(7).trimmed();
if (format != QByteArrayLiteral("32-bit_rle_rgbe")) {
qWarning("HDR format '%s' is not supported", format.constData());
return QByteArray();
}
}
line.clear();
} else {
line.append(c);
}
}
if (p == pEnd) {
qWarning("Malformed HDR image data at property strings");
return QByteArray();
}
// Get the resolution string.
while (p < pEnd) {
char c = *p++;
if (c == '\n')
break;
line.append(c);
}
if (p == pEnd) {
qWarning("Malformed HDR image data at resolution string");
return QByteArray();
}
int w = 0, h = 0;
// We only care about the standard orientation.
if (!sscanf(line.constData(), "-Y %d +X %d", &h, &w)) {
qWarning("Unsupported HDR resolution string '%s'", line.constData());
return QByteArray();
}
if (w <= 0 || h <= 0) {
qWarning("Invalid HDR resolution");
return QByteArray();
}
// output is RGBA32F
const int blockSize = 4 * sizeof(float);
QByteArray data;
data.resize(w * h * blockSize);
typedef unsigned char RGBE[4];
RGBE *scanline = new RGBE[w];
for (int y = 0; y < h; ++y) {
if (pEnd - p < 4) {
qWarning("Unexpected end of HDR data");
delete[] scanline;
return QByteArray();
}
scanline[0][0] = *p++;
scanline[0][1] = *p++;
scanline[0][2] = *p++;
scanline[0][3] = *p++;
if (scanline[0][0] == 2 && scanline[0][1] == 2 && scanline[0][2] < 128) {
// new rle, the first pixel was a dummy
for (int channel = 0; channel < 4; ++channel) {
for (int x = 0; x < w && p < pEnd; ) {
unsigned char c = *p++;
if (c > 128) { // run
if (p < pEnd) {
int repCount = c & 127;
c = *p++;
while (repCount--)
scanline[x++][channel] = c;
}
} else { // not a run
while (c-- && p < pEnd)
scanline[x++][channel] = *p++;
}
}
}
} else {
// old rle
scanline[0][0] = 2;
int bitshift = 0;
int x = 1;
while (x < w && pEnd - p >= 4) {
scanline[x][0] = *p++;
scanline[x][1] = *p++;
scanline[x][2] = *p++;
scanline[x][3] = *p++;
if (scanline[x][0] == 1 && scanline[x][1] == 1 && scanline[x][2] == 1) { // run
int repCount = scanline[x][3] << bitshift;
while (repCount--) {
memcpy(scanline[x], scanline[x - 1], 4);
++x;
}
bitshift += 8;
} else { // not a run
++x;
bitshift = 0;
}
}
}
// adjust for -Y orientation
float *fp = reinterpret_cast<float *>(data.data() + (h - 1 - y) * blockSize * w);
for (int x = 0; x < w; ++x) {
float d = qPow(2.0f, float(scanline[x][3]) - 128.0f);
// r, g, b, a
*fp++ = scanline[x][0] / 256.0f * d;
*fp++ = scanline[x][1] / 256.0f * d;
*fp++ = scanline[x][2] / 256.0f * d;
*fp++ = 1.0f;
}
}
delete[] scanline;
if (size)
*size = QSize(w, h);
return data;
}
static float vertexData[] =
{ // Y up, CCW
-0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 1.0f, 0.0f
};
static quint16 indexData[] =
{
0, 1, 2, 0, 2, 3
};
struct {
QVector<QRhiResource *> releasePool;
QRhiBuffer *vbuf = nullptr;
QRhiBuffer *ibuf = nullptr;
QRhiBuffer *ubuf = nullptr;
QRhiTexture *tex = nullptr;
QRhiSampler *sampler = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
QMatrix4x4 winProj;
} d;
void Window::customInit()
{
if (!m_r->isTextureFormatSupported(QRhiTexture::RGBA32F))
qWarning("RGBA32F texture format is not supported");
QSize size;
QByteArray floatData = loadHdr(QLatin1String(":/OpenfootageNET_fieldairport-512.hdr"), &size);
Q_ASSERT(!floatData.isEmpty());
qDebug() << size;
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData));
d.vbuf->build();
d.releasePool << d.vbuf;
d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(indexData));
d.ibuf->build();
d.releasePool << d.ibuf;
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
d.ubuf->build();
d.releasePool << d.ubuf;
d.tex = m_r->newTexture(QRhiTexture::RGBA32F, size);
d.releasePool << d.tex;
d.tex->build();
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
d.releasePool << d.sampler;
d.sampler->build();
d.srb = m_r->newShaderResourceBindings();
d.releasePool << d.srb;
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler)
});
d.srb->build();
d.ps = m_r->newGraphicsPipeline();
d.releasePool << d.ps;
d.ps->setShaderStages({
{ QRhiGraphicsShaderStage::Vertex, getShader(QLatin1String(":/texture.vert.qsb")) },
{ QRhiGraphicsShaderStage::Fragment, getShader(QLatin1String(":/texture.frag.qsb")) }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 4 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->build();
d.initialUpdates = m_r->nextResourceUpdateBatch();
d.initialUpdates->uploadStaticBuffer(d.vbuf, vertexData);
d.initialUpdates->uploadStaticBuffer(d.ibuf, indexData);
qint32 flip = 1;
d.initialUpdates->updateDynamicBuffer(d.ubuf, 64, 4, &flip);
QRhiTextureUploadDescription desc({ 0, 0, { floatData.constData(), floatData.size() } });
d.initialUpdates->uploadTexture(d.tex, desc);
}
void Window::customRelease()
{
qDeleteAll(d.releasePool);
d.releasePool.clear();
}
void Window::customRender()
{
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
}
if (d.winProj != m_proj) {
d.winProj = m_proj;
QMatrix4x4 mvp = m_proj;
mvp.scale(2.5f);
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
}
const QSize outputSizeInPixels = m_sc->currentPixelSize();
cb->beginPass(m_sc->currentFrameRenderTarget(), QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f), { 1.0f, 0 }, u);
cb->setGraphicsPipeline(d.ps);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16);
cb->drawIndexed(6);
cb->endPass();
}

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
floattexture.cpp
RESOURCES = floattexture.qrc

View File

@ -0,0 +1,7 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="texture.vert.qsb">../shared/texture.vert.qsb</file>
<file alias="texture.frag.qsb">../shared/texture.frag.qsb</file>
<file alias="OpenfootageNET_fieldairport-512.hdr">../shared/OpenfootageNET_fieldairport-512.hdr</file>
</qresource>
</RCC>

View File

@ -0,0 +1,554 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
// This is a compact, minimal, single-file demo of deciding the backend at
// runtime while using the exact same shaders and rendering code without any
// branching whatsoever once the QWindow is up and the RHI is initialized.
#include <QGuiApplication>
#include <QCommandLineParser>
#include <QWindow>
#include <QPlatformSurfaceEvent>
#include <QElapsedTimer>
#include <QtGui/private/qshader_p.h>
#include <QFile>
#include <QtGui/private/qrhinull_p.h>
#ifndef QT_NO_OPENGL
#include <QtGui/private/qrhigles2_p.h>
#include <QOffscreenSurface>
#endif
#if QT_CONFIG(vulkan)
#include <QLoggingCategory>
#include <QtGui/private/qrhivulkan_p.h>
#endif
#ifdef Q_OS_WIN
#include <QtGui/private/qrhid3d11_p.h>
#endif
#ifdef Q_OS_DARWIN
#include <QtGui/private/qrhimetal_p.h>
#endif
static float vertexData[] = {
// Y up (note clipSpaceCorrMatrix in m_proj), CCW
0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
};
static QShader getShader(const QString &name)
{
QFile f(name);
if (f.open(QIODevice::ReadOnly))
return QShader::fromSerialized(f.readAll());
return QShader();
}
enum GraphicsApi
{
OpenGL,
Vulkan,
D3D11,
Metal,
Null
};
static GraphicsApi graphicsApi;
static QString graphicsApiName()
{
switch (graphicsApi) {
case OpenGL:
return QLatin1String("OpenGL 2.x");
case Vulkan:
return QLatin1String("Vulkan");
case D3D11:
return QLatin1String("Direct3D 11");
case Metal:
return QLatin1String("Metal");
case Null:
return QLatin1String("Null");
default:
break;
}
return QString();
}
class Window : public QWindow
{
public:
Window();
~Window();
void init();
void releaseResources();
void resizeSwapChain();
void releaseSwapChain();
void render();
void exposeEvent(QExposeEvent *) override;
bool event(QEvent *) override;
private:
bool m_running = false;
bool m_notExposed = false;
bool m_newlyExposed = false;
QRhi *m_r = nullptr;
bool m_hasSwapChain = false;
QRhiSwapChain *m_sc = nullptr;
QRhiRenderBuffer *m_ds = nullptr;
QRhiRenderPassDescriptor *m_rp = nullptr;
QRhiBuffer *m_vbuf = nullptr;
bool m_vbufReady = false;
QRhiBuffer *m_ubuf = nullptr;
QRhiShaderResourceBindings *m_srb = nullptr;
QRhiGraphicsPipeline *m_ps = nullptr;
QVector<QRhiResource *> releasePool;
QMatrix4x4 m_proj;
float m_rotation = 0;
float m_opacity = 1;
int m_opacityDir = -1;
QElapsedTimer m_timer;
qint64 m_elapsedMs;
int m_elapsedCount;
#ifndef QT_NO_OPENGL
QOffscreenSurface *m_fallbackSurface = nullptr;
#endif
};
Window::Window()
{
// Tell the platform plugin what we want.
switch (graphicsApi) {
case OpenGL:
#if QT_CONFIG(opengl)
setSurfaceType(OpenGLSurface);
setFormat(QRhiGles2InitParams::adjustedFormat());
#endif
break;
case Vulkan:
setSurfaceType(VulkanSurface);
break;
case D3D11:
setSurfaceType(OpenGLSurface); // not a typo
break;
case Metal:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
setSurfaceType(MetalSurface);
#endif
break;
default:
break;
}
}
Window::~Window()
{
releaseResources();
}
void Window::exposeEvent(QExposeEvent *)
{
// initialize and start rendering when the window becomes usable for graphics purposes
if (isExposed() && !m_running) {
m_running = true;
init();
resizeSwapChain();
}
// stop pushing frames when not exposed (or size is 0)
if ((!isExposed() || (m_hasSwapChain && m_sc->surfacePixelSize().isEmpty())) && m_running)
m_notExposed = true;
// continue when exposed again and the surface has a valid size.
// note that the surface size can be (0, 0) even though size() reports a valid one...
if (isExposed() && m_running && m_notExposed && !m_sc->surfacePixelSize().isEmpty()) {
m_notExposed = false;
m_newlyExposed = true;
}
// always render a frame on exposeEvent() (when exposed) in order to update
// immediately on window resize.
if (isExposed() && !m_sc->surfacePixelSize().isEmpty())
render();
}
bool Window::event(QEvent *e)
{
switch (e->type()) {
case QEvent::UpdateRequest:
render();
break;
case QEvent::PlatformSurface:
// this is the proper time to tear down the swapchain (while the native window and surface are still around)
if (static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed)
releaseSwapChain();
break;
default:
break;
}
return QWindow::event(e);
}
void Window::init()
{
if (graphicsApi == Null) {
QRhiNullInitParams params;
m_r = QRhi::create(QRhi::Null, &params);
}
#ifndef QT_NO_OPENGL
if (graphicsApi == OpenGL) {
m_fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
QRhiGles2InitParams params;
params.fallbackSurface = m_fallbackSurface;
params.window = this;
m_r = QRhi::create(QRhi::OpenGLES2, &params);
}
#endif
#if QT_CONFIG(vulkan)
if (graphicsApi == Vulkan) {
QRhiVulkanInitParams params;
params.inst = vulkanInstance();
params.window = this;
m_r = QRhi::create(QRhi::Vulkan, &params);
}
#endif
#ifdef Q_OS_WIN
if (graphicsApi == D3D11) {
QRhiD3D11InitParams params;
m_r = QRhi::create(QRhi::D3D11, &params);
}
#endif
#ifdef Q_OS_DARWIN
if (graphicsApi == Metal) {
QRhiMetalInitParams params;
m_r = QRhi::create(QRhi::Metal, &params);
}
#endif
if (!m_r)
qFatal("Failed to create RHI backend");
// now onto the backend-independent init
m_sc = m_r->newSwapChain();
// allow depth-stencil, although we do not actually enable depth test/write for the triangle
m_ds = m_r->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
QSize(), // no need to set the size yet
1,
QRhiRenderBuffer::UsedWithSwapChainOnly);
releasePool << m_ds;
m_sc->setWindow(this);
m_sc->setDepthStencil(m_ds);
m_rp = m_sc->newCompatibleRenderPassDescriptor();
releasePool << m_rp;
m_sc->setRenderPassDescriptor(m_rp);
m_vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData));
releasePool << m_vbuf;
m_vbuf->build();
m_vbufReady = false;
m_ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
releasePool << m_ubuf;
m_ubuf->build();
m_srb = m_r->newShaderResourceBindings();
releasePool << m_srb;
m_srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, m_ubuf)
});
m_srb->build();
m_ps = m_r->newGraphicsPipeline();
releasePool << m_ps;
QRhiGraphicsPipeline::TargetBlend premulAlphaBlend;
premulAlphaBlend.enable = true;
m_ps->setTargetBlends({ premulAlphaBlend });
const QShader vs = getShader(QLatin1String(":/color.vert.qsb"));
if (!vs.isValid())
qFatal("Failed to load shader pack (vertex)");
const QShader fs = getShader(QLatin1String(":/color.frag.qsb"));
if (!fs.isValid())
qFatal("Failed to load shader pack (fragment)");
m_ps->setShaderStages({
{ QRhiGraphicsShaderStage::Vertex, vs },
{ QRhiGraphicsShaderStage::Fragment, fs }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 5 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) }
});
m_ps->setVertexInputLayout(inputLayout);
m_ps->setShaderResourceBindings(m_srb);
m_ps->setRenderPassDescriptor(m_rp);
m_ps->build();
}
void Window::releaseResources()
{
qDeleteAll(releasePool);
releasePool.clear();
delete m_sc; // native swapchain is likely released already
m_sc = nullptr;
delete m_r;
#ifndef QT_NO_OPENGL
delete m_fallbackSurface;
#endif
}
void Window::resizeSwapChain()
{
const QSize outputSize = m_sc->surfacePixelSize();
m_ds->setPixelSize(outputSize);
m_ds->build(); // == m_ds->release(); m_ds->build();
m_hasSwapChain = m_sc->buildOrResize();
m_elapsedMs = 0;
m_elapsedCount = 0;
m_proj = m_r->clipSpaceCorrMatrix();
m_proj.perspective(45.0f, outputSize.width() / (float) outputSize.height(), 0.01f, 100.0f);
m_proj.translate(0, 0, -4);
}
void Window::releaseSwapChain()
{
if (m_hasSwapChain) {
m_hasSwapChain = false;
m_sc->release();
}
}
void Window::render()
{
if (!m_hasSwapChain || m_notExposed)
return;
// If the window got resized or got newly exposed, resize the swapchain.
// (the newly-exposed case is not actually required by some
// platforms/backends, but f.ex. Vulkan on Windows seems to need it)
if (m_sc->currentPixelSize() != m_sc->surfacePixelSize() || m_newlyExposed) {
resizeSwapChain();
if (!m_hasSwapChain)
return;
m_newlyExposed = false;
}
// Start a new frame. This is where we block when too far ahead of
// GPU/present, and that's what throttles the thread to the refresh rate.
// (except for OpenGL where it happens either in endFrame or somewhere else
// depending on the GL implementation)
QRhi::FrameOpResult r = m_r->beginFrame(m_sc);
if (r == QRhi::FrameOpSwapChainOutOfDate) {
resizeSwapChain();
if (!m_hasSwapChain)
return;
r = m_r->beginFrame(m_sc);
}
if (r != QRhi::FrameOpSuccess) {
requestUpdate();
return;
}
if (m_elapsedCount)
m_elapsedMs += m_timer.elapsed();
m_timer.restart();
m_elapsedCount += 1;
if (m_elapsedMs >= 4000) {
qDebug("%f", m_elapsedCount / 4.0f);
m_elapsedMs = 0;
m_elapsedCount = 0;
}
// Set up buffer updates.
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (!m_vbufReady) {
m_vbufReady = true;
u->uploadStaticBuffer(m_vbuf, vertexData);
}
m_rotation += 1.0f;
QMatrix4x4 mvp = m_proj;
mvp.rotate(m_rotation, 0, 1, 0);
u->updateDynamicBuffer(m_ubuf, 0, 64, mvp.constData());
m_opacity += m_opacityDir * 0.005f;
if (m_opacity < 0.0f || m_opacity > 1.0f) {
m_opacityDir *= -1;
m_opacity = qBound(0.0f, m_opacity, 1.0f);
}
u->updateDynamicBuffer(m_ubuf, 64, 4, &m_opacity);
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
const QSize outputSizeInPixels = m_sc->currentPixelSize();
// Apply buffer updates, clear, start the renderpass (where applicable).
cb->beginPass(m_sc->currentFrameRenderTarget(), QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f), { 1.0f, 0 }, u);
cb->setGraphicsPipeline(m_ps);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(3);
cb->endPass();
// Submit.
m_r->endFrame(m_sc);
requestUpdate(); // render continuously, throttled by the presentation rate (due to beginFrame above)
}
int main(int argc, char **argv)
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
// Defaults.
#if defined(Q_OS_WIN)
graphicsApi = D3D11;
#elif defined(Q_OS_DARWIN)
graphicsApi = Metal;
#elif QT_CONFIG(vulkan)
graphicsApi = Vulkan;
#else
graphicsApi = OpenGL;
#endif
// Allow overriding via the command line.
QCommandLineParser cmdLineParser;
cmdLineParser.addHelpOption();
QCommandLineOption glOption({ "g", "opengl" }, QLatin1String("OpenGL (2.x)"));
cmdLineParser.addOption(glOption);
QCommandLineOption vkOption({ "v", "vulkan" }, QLatin1String("Vulkan"));
cmdLineParser.addOption(vkOption);
QCommandLineOption d3dOption({ "d", "d3d11" }, QLatin1String("Direct3D 11"));
cmdLineParser.addOption(d3dOption);
QCommandLineOption mtlOption({ "m", "metal" }, QLatin1String("Metal"));
cmdLineParser.addOption(mtlOption);
QCommandLineOption nullOption({ "n", "null" }, QLatin1String("Null"));
cmdLineParser.addOption(nullOption);
cmdLineParser.process(app);
if (cmdLineParser.isSet(glOption))
graphicsApi = OpenGL;
if (cmdLineParser.isSet(vkOption))
graphicsApi = Vulkan;
if (cmdLineParser.isSet(d3dOption))
graphicsApi = D3D11;
if (cmdLineParser.isSet(mtlOption))
graphicsApi = Metal;
if (cmdLineParser.isSet(nullOption))
graphicsApi = Null;
// Vulkan setup.
#if QT_CONFIG(vulkan)
QVulkanInstance inst;
if (graphicsApi == Vulkan) {
if (!inst.create()) {
qWarning("Failed to create Vulkan instance, switching to OpenGL");
graphicsApi = OpenGL;
}
}
#endif
// Create and show the window.
Window w;
#if QT_CONFIG(vulkan)
if (graphicsApi == Vulkan)
w.setVulkanInstance(&inst);
#endif
w.resize(1280, 720);
w.setTitle(graphicsApiName());
w.show();
// Window::event() will not get invoked when the
// PlatformSurfaceAboutToBeDestroyed event is sent during the QWindow
// destruction. That happens only when exiting via app::quit() instead of
// the more common QWindow::close(). Take care of it: if the
// QPlatformWindow is still around (there was no close() yet), get rid of
// the swapchain while it's not too late.
if (w.handle())
w.releaseSwapChain();
return app.exec();
}

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
hellominimalcrossgfxtriangle.cpp
RESOURCES = hellominimalcrossgfxtriangle.qrc

View File

@ -0,0 +1,6 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="color.vert.qsb">../shared/color.vert.qsb</file>
<file alias="color.frag.qsb">../shared/color.frag.qsb</file>
</qresource>
</RCC>

View File

@ -0,0 +1,2 @@
qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c mrt.vert -o mrt.vert.qsb
qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c mrt.frag -o mrt.frag.qsb

View File

@ -0,0 +1,296 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "../shared/examplefw.h"
// Exercises MRT, by attaching 4 textures to a single QRhiTextureRenderTarget.
// The fragment shader outputs to all four targets.
// The textures are then used to render 4 quads, so their contents can be confirmed.
const int ATTCOUNT = 4;
static float quadVertexData[] =
{ // Y up, CCW
-0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 1.0f, 0.0f
};
static quint16 quadIndexData[] =
{
0, 1, 2, 0, 2, 3
};
static float triangleData[] =
{ // Y up, CCW
0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
};
struct {
QVector<QRhiResource *> releasePool;
QRhiBuffer *vbuf = nullptr;
QRhiBuffer *ibuf = nullptr;
QRhiBuffer *ubuf = nullptr;
QRhiTextureRenderTarget *rt = nullptr;
QRhiRenderPassDescriptor *rtRp = nullptr;
QRhiSampler *sampler = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
QMatrix4x4 winProj;
struct PerColorBuffer {
QRhiTexture *tex = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
};
PerColorBuffer colData[ATTCOUNT];
QRhiBuffer *triUbuf = nullptr;
QRhiShaderResourceBindings *triSrb = nullptr;
QRhiGraphicsPipeline *triPs = nullptr;
float triRot = 0;
QMatrix4x4 triBaseMvp;
} d;
void Window::customInit()
{
qDebug("Max color attachments: %d", m_r->resourceLimit(QRhi::MaxColorAttachments));
if (m_r->resourceLimit(QRhi::MaxColorAttachments) < 4)
qWarning("MRT is not supported");
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(quadVertexData) + sizeof(triangleData));
d.vbuf->build();
d.releasePool << d.vbuf;
d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(quadIndexData));
d.ibuf->build();
d.releasePool << d.ibuf;
const int oneRoundedUniformBlockSize = m_r->ubufAligned(68);
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, oneRoundedUniformBlockSize * ATTCOUNT);
d.ubuf->build();
d.releasePool << d.ubuf;
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
d.releasePool << d.sampler;
d.sampler->build();
for (int i = 0; i < ATTCOUNT; ++i) {
QRhiTexture *tex = m_r->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 1, QRhiTexture::RenderTarget);
d.releasePool << tex;
tex->build();
QRhiShaderResourceBindings *srb = m_r->newShaderResourceBindings();
d.releasePool << srb;
srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
d.ubuf, i * oneRoundedUniformBlockSize, 68),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, tex, d.sampler)
});
srb->build();
d.colData[i].tex = tex;
d.colData[i].srb = srb;
}
QRhiTextureRenderTargetDescription rtDesc;
QVector<QRhiColorAttachment> att;
for (int i = 0; i < ATTCOUNT; ++i)
att.append(QRhiColorAttachment(d.colData[i].tex));
rtDesc.setColorAttachments(att);
d.rt = m_r->newTextureRenderTarget(rtDesc);
d.releasePool << d.rt;
d.rtRp = d.rt->newCompatibleRenderPassDescriptor();
d.releasePool << d.rtRp;
d.rt->setRenderPassDescriptor(d.rtRp);
d.rt->build();
d.ps = m_r->newGraphicsPipeline();
d.releasePool << d.ps;
d.ps->setShaderStages({
{ QRhiGraphicsShaderStage::Vertex, getShader(QLatin1String(":/texture.vert.qsb")) },
{ QRhiGraphicsShaderStage::Fragment, getShader(QLatin1String(":/texture.frag.qsb")) }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 4 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.colData[0].srb); // all of them are layout-compatible
d.ps->setRenderPassDescriptor(m_rp);
d.ps->build();
d.initialUpdates = m_r->nextResourceUpdateBatch();
d.initialUpdates->uploadStaticBuffer(d.vbuf, 0, sizeof(quadVertexData), quadVertexData);
d.initialUpdates->uploadStaticBuffer(d.vbuf, sizeof(quadVertexData), sizeof(triangleData), triangleData);
d.initialUpdates->uploadStaticBuffer(d.ibuf, quadIndexData);
qint32 flip = m_r->isYUpInFramebuffer() ? 1 : 0;
for (int i = 0; i < ATTCOUNT; ++i)
d.initialUpdates->updateDynamicBuffer(d.ubuf, i * oneRoundedUniformBlockSize + 64, 4, &flip);
// triangle
d.triUbuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
d.releasePool << d.triUbuf;
d.triUbuf->build();
d.triSrb = m_r->newShaderResourceBindings();
d.releasePool << d.triSrb;
d.triSrb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.triUbuf)
});
d.triSrb->build();
d.triPs = m_r->newGraphicsPipeline();
d.releasePool << d.triPs;
d.triPs->setShaderStages({
{ QRhiGraphicsShaderStage::Vertex, getShader(QLatin1String(":/mrt.vert.qsb")) },
{ QRhiGraphicsShaderStage::Fragment, getShader(QLatin1String(":/mrt.frag.qsb")) }
});
QVector<QRhiGraphicsPipeline::TargetBlend> blends;
for (int i = 0; i < ATTCOUNT; ++i) {
QRhiGraphicsPipeline::TargetBlend blend;
blends.append(blend);
}
d.triPs->setTargetBlends(blends);
inputLayout.setBindings({
{ 5 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) }
});
d.triPs->setVertexInputLayout(inputLayout);
d.triPs->setShaderResourceBindings(d.triSrb);
d.triPs->setRenderPassDescriptor(d.rtRp);
d.triPs->build();
d.triBaseMvp = m_r->clipSpaceCorrMatrix();
d.triBaseMvp.perspective(45.0f, d.rt->pixelSize().width() / float(d.rt->pixelSize().height()), 0.01f, 1000.0f);
d.triBaseMvp.translate(0, 0, -2);
float opacity = 1.0f;
d.initialUpdates->updateDynamicBuffer(d.triUbuf, 64, 4, &opacity);
}
void Window::customRelease()
{
qDeleteAll(d.releasePool);
d.releasePool.clear();
}
void Window::customRender()
{
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
}
QMatrix4x4 triMvp = d.triBaseMvp;
triMvp.rotate(d.triRot, 0, 1, 0);
d.triRot += 1;
u->updateDynamicBuffer(d.triUbuf, 0, 64, triMvp.constData());
cb->beginPass(d.rt, QColor::fromRgbF(0.5f, 0.2f, 0.0f, 1.0f), { 1.0f, 0 }, u);
cb->setGraphicsPipeline(d.triPs);
cb->setViewport({ 0, 0, float(d.rt->pixelSize().width()), float(d.rt->pixelSize().height()) });
cb->setShaderResources();
QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, sizeof(quadVertexData));
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(3);
cb->endPass();
u = m_r->nextResourceUpdateBatch();
if (d.winProj != m_proj) {
d.winProj = m_proj;
const int oneRoundedUniformBlockSize = m_r->ubufAligned(68);
for (int i = 0; i < ATTCOUNT; ++i) {
QMatrix4x4 mvp = m_proj;
switch (i) {
case 0:
mvp.translate(-2.0f, 0, 0);
break;
case 1:
mvp.translate(-0.8f, 0, 0);
break;
case 2:
mvp.translate(0.4f, 0, 0);
break;
case 3:
mvp.translate(1.6f, 0, 0);
break;
default:
Q_UNREACHABLE();
break;
}
u->updateDynamicBuffer(d.ubuf, i * oneRoundedUniformBlockSize, 64, mvp.constData());
}
}
const QSize outputSizeInPixels = m_sc->currentPixelSize();
cb->beginPass(m_sc->currentFrameRenderTarget(), QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f), { 1.0f, 0 }, u);
cb->setGraphicsPipeline(d.ps);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
vbufBinding.second = 0;
cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16);
for (int i = 0; i < ATTCOUNT; ++i) {
cb->setShaderResources(d.colData[i].srb);
cb->drawIndexed(6);
}
cb->endPass();
}

View File

@ -0,0 +1,22 @@
#version 440
layout(location = 0) in vec3 v_color;
layout(location = 0) out vec4 c0;
layout(location = 1) out vec4 c1;
layout(location = 2) out vec4 c2;
layout(location = 3) out vec4 c3;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
float opacity;
} ubuf;
void main()
{
vec4 c = vec4(v_color * ubuf.opacity, ubuf.opacity);
c0 = vec4(c.r, 0, 0, c.a);
c1 = vec4(0, c.g, 0, c.a);
c2 = vec4(0, 0, c.b, c.a);
c3 = c;
}

Binary file not shown.

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
mrt.cpp
RESOURCES = mrt.qrc

View File

@ -0,0 +1,8 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>mrt.vert.qsb</file>
<file>mrt.frag.qsb</file>
<file alias="texture.vert.qsb">../shared/texture.vert.qsb</file>
<file alias="texture.frag.qsb">../shared/texture.frag.qsb</file>
</qresource>
</RCC>

View File

@ -0,0 +1,19 @@
#version 440
layout(location = 0) in vec4 position;
layout(location = 1) in vec3 color;
layout(location = 0) out vec3 v_color;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
float opacity;
} ubuf;
out gl_PerVertex { vec4 gl_Position; };
void main()
{
v_color = color;
gl_Position = ubuf.mvp * position;
}

Binary file not shown.

View File

@ -0,0 +1,259 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "../shared/examplefw.h"
// Uses a multisample renderbuffer (whatever that may be on a given backend) to
// render to and then resolves the samples into a non-multisample texture.
static float vertexData[] =
{ // Y up, CCW
-0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 1.0f, 0.0f
};
static quint16 indexData[] =
{
0, 1, 2, 0, 2, 3
};
static float triangleData[] =
{ // Y up, CCW
0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
};
struct {
QVector<QRhiResource *> releasePool;
QRhiBuffer *vbuf = nullptr;
QRhiBuffer *ibuf = nullptr;
QRhiBuffer *ubuf = nullptr;
QRhiRenderBuffer *rb = nullptr;
QRhiTextureRenderTarget *rt = nullptr;
QRhiRenderPassDescriptor *rtRp = nullptr;
QRhiTexture *tex = nullptr;
QRhiSampler *sampler = nullptr;
QRhiBuffer *triUbuf = nullptr;
QRhiShaderResourceBindings *triSrb = nullptr;
QRhiGraphicsPipeline *triPs = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
QMatrix4x4 triBaseMvp;
float triRot = 0;
QMatrix4x4 winProj;
} d;
void Window::customInit()
{
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData) + sizeof(triangleData));
d.vbuf->build();
d.releasePool << d.vbuf;
d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(indexData));
d.ibuf->build();
d.releasePool << d.ibuf;
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
d.ubuf->build();
d.releasePool << d.ubuf;
d.rb = m_r->newRenderBuffer(QRhiRenderBuffer::Color, QSize(512, 512), 4); // 4x MSAA
d.rb->build();
d.releasePool << d.rb;
// the non-msaa texture that will be the destination in the resolve
d.tex = m_r->newTexture(QRhiTexture::RGBA8, d.rb->pixelSize(), 1, QRhiTexture::RenderTarget);
d.releasePool << d.tex;
d.tex->build();
// rb is multisample, instead of writing out the msaa data into it,
// resolve into d.tex at the end of each render pass
QRhiTextureRenderTargetDescription rtDesc;
QRhiColorAttachment rtAtt(d.rb);
rtAtt.setResolveTexture(d.tex);
rtDesc.setColorAttachments({ rtAtt });
d.rt = m_r->newTextureRenderTarget(rtDesc);
d.releasePool << d.rt;
d.rtRp = d.rt->newCompatibleRenderPassDescriptor();
d.releasePool << d.rtRp;
d.rt->setRenderPassDescriptor(d.rtRp);
d.rt->build();
d.triUbuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
d.releasePool << d.triUbuf;
d.triUbuf->build();
d.triSrb = m_r->newShaderResourceBindings();
d.releasePool << d.triSrb;
d.triSrb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.triUbuf)
});
d.triSrb->build();
d.triPs = m_r->newGraphicsPipeline();
d.releasePool << d.triPs;
d.triPs->setSampleCount(4); // must match the render target
d.triPs->setShaderStages({
{ QRhiGraphicsShaderStage::Vertex, getShader(QLatin1String(":/color.vert.qsb")) },
{ QRhiGraphicsShaderStage::Fragment, getShader(QLatin1String(":/color.frag.qsb")) }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 5 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) }
});
d.triPs->setVertexInputLayout(inputLayout);
d.triPs->setShaderResourceBindings(d.triSrb);
d.triPs->setRenderPassDescriptor(d.rtRp);
d.triPs->build();
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
d.releasePool << d.sampler;
d.sampler->build();
d.srb = m_r->newShaderResourceBindings();
d.releasePool << d.srb;
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler)
});
d.srb->build();
d.ps = m_r->newGraphicsPipeline();
d.releasePool << d.ps;
d.ps->setShaderStages({
{ QRhiGraphicsShaderStage::Vertex, getShader(QLatin1String(":/texture.vert.qsb")) },
{ QRhiGraphicsShaderStage::Fragment, getShader(QLatin1String(":/texture.frag.qsb")) }
});
inputLayout.setBindings({
{ 4 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->build();
d.initialUpdates = m_r->nextResourceUpdateBatch();
d.initialUpdates->uploadStaticBuffer(d.vbuf, 0, sizeof(vertexData), vertexData);
d.initialUpdates->uploadStaticBuffer(d.vbuf, sizeof(vertexData), sizeof(triangleData), triangleData);
d.initialUpdates->uploadStaticBuffer(d.ibuf, indexData);
d.triBaseMvp = m_r->clipSpaceCorrMatrix();
d.triBaseMvp.perspective(45.0f, d.rb->pixelSize().width() / float(d.rb->pixelSize().height()), 0.01f, 1000.0f);
d.triBaseMvp.translate(0, 0, -2);
float opacity = 1.0f;
d.initialUpdates->updateDynamicBuffer(d.triUbuf, 64, 4, &opacity);
qint32 flip = m_r->isYUpInFramebuffer() ? 1 : 0;
d.initialUpdates->updateDynamicBuffer(d.ubuf, 64, 4, &flip);
}
void Window::customRelease()
{
qDeleteAll(d.releasePool);
d.releasePool.clear();
}
void Window::customRender()
{
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
}
QMatrix4x4 triMvp = d.triBaseMvp;
triMvp.rotate(d.triRot, 0, 1, 0);
d.triRot += 1;
u->updateDynamicBuffer(d.triUbuf, 0, 64, triMvp.constData());
if (d.winProj != m_proj) {
d.winProj = m_proj;
QMatrix4x4 mvp = m_proj;
mvp.scale(2.5f);
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
}
// offscreen (triangle, msaa)
cb->beginPass(d.rt, QColor::fromRgbF(0.5f, 0.2f, 0.0f, 1.0f), { 1.0f, 0 }, u);
cb->setGraphicsPipeline(d.triPs);
cb->setViewport({ 0, 0, float(d.rb->pixelSize().width()), float(d.rb->pixelSize().height()) });
cb->setShaderResources();
QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, sizeof(vertexData));
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(3);
cb->endPass();
// onscreen (quad)
const QSize outputSizeInPixels = m_sc->currentPixelSize();
cb->beginPass(m_sc->currentFrameRenderTarget(), QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f), { 1.0f, 0 });
cb->setGraphicsPipeline(d.ps);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
cb->setShaderResources();
vbufBinding.second = 0;
cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16);
cb->drawIndexed(6);
cb->endPass();
}

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
msaarenderbuffer.cpp
RESOURCES = msaarenderbuffer.qrc

View File

@ -0,0 +1,8 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="color.vert.qsb">../shared/color.vert.qsb</file>
<file alias="color.frag.qsb">../shared/color.frag.qsb</file>
<file alias="texture.vert.qsb">../shared/texture.vert.qsb</file>
<file alias="texture.frag.qsb">../shared/texture.frag.qsb</file>
</qresource>
</RCC>

View File

@ -0,0 +1,329 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "../shared/examplefw.h"
// Renders into a non-multisample and a multisample (4x) texture, and then uses
// those textures to draw two quads. Note that this uses an MSAA sampler in the
// shader, not resolves. Not supported on the GL(ES) backend atm.
static float vertexData[] =
{ // Y up, CCW
-0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 1.0f, 0.0f
};
static quint16 indexData[] =
{
0, 1, 2, 0, 2, 3
};
static float triangleData[] =
{ // Y up, CCW
0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
};
const int UBUFSZ = 68;
struct {
QVector<QRhiResource *> releasePool;
QRhiBuffer *vbuf = nullptr;
QRhiBuffer *ibuf = nullptr;
QRhiBuffer *ubuf = nullptr;
QRhiTexture *tex = nullptr;
QRhiTexture *msaaTex = nullptr;
QRhiSampler *sampler = nullptr;
QRhiShaderResourceBindings *srbLeft = nullptr;
QRhiShaderResourceBindings *srbRight = nullptr;
QRhiGraphicsPipeline *psLeft = nullptr;
QRhiGraphicsPipeline *psRight = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
int rightOfs;
QMatrix4x4 winProj;
QMatrix4x4 triBaseMvp;
float triRot = 0;
QRhiShaderResourceBindings *triSrb = nullptr;
QRhiGraphicsPipeline *msaaTriPs = nullptr;
QRhiGraphicsPipeline *triPs = nullptr;
QRhiBuffer *triUbuf = nullptr;
QRhiTextureRenderTarget *msaaRt = nullptr;
QRhiRenderPassDescriptor *msaaRtRp = nullptr;
QRhiTextureRenderTarget *rt = nullptr;
QRhiRenderPassDescriptor *rtRp = nullptr;
} d;
//#define NO_MSAA
void Window::customInit()
{
if (!m_r->isFeatureSupported(QRhi::MultisampleTexture))
qFatal("Multisample textures not supported by this backend");
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData) + sizeof(triangleData));
d.releasePool << d.vbuf;
d.vbuf->build();
d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(indexData));
d.releasePool << d.ibuf;
d.ibuf->build();
d.rightOfs = m_r->ubufAligned(UBUFSZ);
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, d.rightOfs + UBUFSZ);
d.releasePool << d.ubuf;
d.ubuf->build();
d.tex = m_r->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 1, QRhiTexture::RenderTarget);
d.releasePool << d.tex;
d.tex->build();
#ifndef NO_MSAA
d.msaaTex = m_r->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 4, QRhiTexture::RenderTarget);
#else
d.msaaTex = m_r->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 1, QRhiTexture::RenderTarget);
#endif
d.releasePool << d.msaaTex;
d.msaaTex->build();
d.initialUpdates = m_r->nextResourceUpdateBatch();
d.initialUpdates->uploadStaticBuffer(d.vbuf, 0, sizeof(vertexData), vertexData);
d.initialUpdates->uploadStaticBuffer(d.vbuf, sizeof(vertexData), sizeof(triangleData), triangleData);
d.initialUpdates->uploadStaticBuffer(d.ibuf, indexData);
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
d.releasePool << d.sampler;
d.sampler->build();
d.srbLeft = m_r->newShaderResourceBindings();
d.releasePool << d.srbLeft;
d.srbLeft->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf, 0, UBUFSZ),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler)
});
d.srbLeft->build();
d.srbRight = m_r->newShaderResourceBindings();
d.releasePool << d.srbRight;
d.srbRight->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf, d.rightOfs, UBUFSZ),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.msaaTex, d.sampler)
});
d.srbRight->build();
d.psLeft = m_r->newGraphicsPipeline();
d.releasePool << d.psLeft;
d.psLeft->setShaderStages({
{ QRhiGraphicsShaderStage::Vertex, getShader(QLatin1String(":/texture.vert.qsb")) },
{ QRhiGraphicsShaderStage::Fragment, getShader(QLatin1String(":/texture.frag.qsb")) }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({ { 4 * sizeof(float) } });
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
});
d.psLeft->setVertexInputLayout(inputLayout);
d.psLeft->setShaderResourceBindings(d.srbLeft);
d.psLeft->setRenderPassDescriptor(m_rp);
d.psLeft->build();
d.psRight = m_r->newGraphicsPipeline();
d.releasePool << d.psRight;
d.psRight->setShaderStages({
{ QRhiGraphicsShaderStage::Vertex, getShader(QLatin1String(":/texture.vert.qsb")) },
#ifndef NO_MSAA
{ QRhiGraphicsShaderStage::Fragment, getShader(QLatin1String(":/texture_ms4.frag.qsb")) }
#else
{ QRhiGraphicsShaderStage::Fragment, getShader(QLatin1String(":/texture.frag.qsb")) }
#endif
});
d.psRight->setVertexInputLayout(d.psLeft->vertexInputLayout());
d.psRight->setShaderResourceBindings(d.srbRight);
d.psRight->setRenderPassDescriptor(m_rp);
d.psRight->build();
// set up the offscreen triangle that goes into tex and msaaTex
d.triUbuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
d.releasePool << d.triUbuf;
d.triUbuf->build();
d.triSrb = m_r->newShaderResourceBindings();
d.releasePool << d.triSrb;
d.triSrb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.triUbuf)
});
d.triSrb->build();
d.rt = m_r->newTextureRenderTarget({ d.tex });
d.releasePool << d.rt;
d.rtRp = d.rt->newCompatibleRenderPassDescriptor();
d.releasePool << d.rtRp;
d.rt->setRenderPassDescriptor(d.rtRp);
d.rt->build();
d.msaaRt = m_r->newTextureRenderTarget({ d.msaaTex });
d.releasePool << d.msaaRt;
d.msaaRtRp = d.msaaRt->newCompatibleRenderPassDescriptor();
d.releasePool << d.msaaRtRp;
d.msaaRt->setRenderPassDescriptor(d.msaaRtRp);
d.msaaRt->build();
d.triPs = m_r->newGraphicsPipeline();
d.releasePool << d.triPs;
d.triPs->setSampleCount(1);
d.triPs->setShaderStages({
{ QRhiGraphicsShaderStage::Vertex, getShader(QLatin1String(":/color.vert.qsb")) },
{ QRhiGraphicsShaderStage::Fragment, getShader(QLatin1String(":/color.frag.qsb")) }
});
inputLayout.setBindings({
{ 5 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) }
});
d.triPs->setVertexInputLayout(inputLayout);
d.triPs->setShaderResourceBindings(d.triSrb);
d.triPs->setRenderPassDescriptor(d.rtRp);
d.triPs->build();
d.msaaTriPs = m_r->newGraphicsPipeline();
d.releasePool << d.msaaTriPs;
#ifndef NO_MSAA
d.msaaTriPs->setSampleCount(4);
#else
d.msaaTriPs->setSampleCount(1);
#endif
d.msaaTriPs->setShaderStages(d.triPs->shaderStages());
d.msaaTriPs->setVertexInputLayout(d.triPs->vertexInputLayout());
d.msaaTriPs->setShaderResourceBindings(d.triSrb);
d.msaaTriPs->setRenderPassDescriptor(d.msaaRtRp);
d.msaaTriPs->build();
}
void Window::customRelease()
{
qDeleteAll(d.releasePool);
d.releasePool.clear();
}
void Window::customRender()
{
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
// onscreen ubuf
qint32 flip = m_r->isYUpInFramebuffer() ? 1 : 0;
u->updateDynamicBuffer(d.ubuf, 64, 4, &flip);
u->updateDynamicBuffer(d.ubuf, d.rightOfs + 64, 4, &flip);
// offscreen ubuf
d.triBaseMvp = m_r->clipSpaceCorrMatrix();
d.triBaseMvp.perspective(45.0f, d.msaaTex->pixelSize().width() / float(d.msaaTex->pixelSize().height()), 0.01f, 1000.0f);
d.triBaseMvp.translate(0, 0, -2);
float opacity = 1.0f;
u->updateDynamicBuffer(d.triUbuf, 64, 4, &opacity);
}
if (d.winProj != m_proj) {
// onscreen buf, window size dependent
d.winProj = m_proj;
QMatrix4x4 mvp = m_proj;
mvp.scale(2);
mvp.translate(-0.8f, 0, 0);
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
mvp.translate(1.6f, 0, 0);
u->updateDynamicBuffer(d.ubuf, d.rightOfs, 64, mvp.constData());
}
// offscreen buf, apply the rotation on every frame
QMatrix4x4 triMvp = d.triBaseMvp;
triMvp.rotate(d.triRot, 0, 1, 0);
d.triRot += 1;
u->updateDynamicBuffer(d.triUbuf, 0, 64, triMvp.constData());
cb->resourceUpdate(u); // could have passed u to beginPass but exercise this one too
// offscreen
cb->beginPass(d.rt, QColor::fromRgbF(0.5f, 0.2f, 0.0f, 1.0f), { 1.0f, 0 });
cb->setGraphicsPipeline(d.triPs);
cb->setViewport({ 0, 0, float(d.msaaTex->pixelSize().width()), float(d.msaaTex->pixelSize().height()) });
cb->setShaderResources();
QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, sizeof(vertexData));
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(3);
cb->endPass();
// offscreen msaa
cb->beginPass(d.msaaRt, QColor::fromRgbF(0.5f, 0.2f, 0.0f, 1.0f), { 1.0f, 0 });
cb->setGraphicsPipeline(d.msaaTriPs);
cb->setViewport({ 0, 0, float(d.msaaTex->pixelSize().width()), float(d.msaaTex->pixelSize().height()) });
cb->setShaderResources();
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(3);
cb->endPass();
// onscreen
const QSize outputSizeInPixels = m_sc->currentPixelSize();
cb->beginPass(m_sc->currentFrameRenderTarget(), QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f), { 1.0f, 0 });
cb->setGraphicsPipeline(d.psLeft); // showing the non-msaa version
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
cb->setShaderResources();
vbufBinding.second = 0;
cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16);
cb->drawIndexed(6);
cb->setGraphicsPipeline(d.psRight); // showing the msaa version, resolved in the shader
cb->setShaderResources();
cb->drawIndexed(6);
cb->endPass();
}

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private
SOURCES = \
msaatexture.cpp
RESOURCES = msaatexture.qrc

View File

@ -0,0 +1,9 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="color.vert.qsb">../shared/color.vert.qsb</file>
<file alias="color.frag.qsb">../shared/color.frag.qsb</file>
<file alias="texture.vert.qsb">../shared/texture.vert.qsb</file>
<file alias="texture.frag.qsb">../shared/texture.frag.qsb</file>
<file alias="texture_ms4.frag.qsb">../shared/texture_ms4.frag.qsb</file>
</qresource>
</RCC>

View File

@ -0,0 +1,644 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QCommandLineParser>
#include <QWindow>
#include <QPlatformSurfaceEvent>
#include <QElapsedTimer>
#include <QtGui/private/qshader_p.h>
#include <QFile>
#ifndef QT_NO_OPENGL
#include <QtGui/private/qrhigles2_p.h>
#include <QOffscreenSurface>
#endif
#if QT_CONFIG(vulkan)
#include <QLoggingCategory>
#include <QtGui/private/qrhivulkan_p.h>
#endif
#ifdef Q_OS_WIN
#include <QtGui/private/qrhid3d11_p.h>
#endif
#ifdef Q_OS_DARWIN
#include <QtGui/private/qrhimetal_p.h>
#endif
enum GraphicsApi
{
OpenGL,
Vulkan,
D3D11,
Metal
};
static GraphicsApi graphicsApi;
static QString graphicsApiName()
{
switch (graphicsApi) {
case OpenGL:
return QLatin1String("OpenGL 2.x");
case Vulkan:
return QLatin1String("Vulkan");
case D3D11:
return QLatin1String("Direct3D 11");
case Metal:
return QLatin1String("Metal");
default:
break;
}
return QString();
}
static struct {
#if QT_CONFIG(vulkan)
QVulkanInstance *instance = nullptr;
#endif
QRhi *r = nullptr;
#ifndef QT_NO_OPENGL
QOffscreenSurface *fallbackSurface = nullptr;
#endif
} r;
void createRhi()
{
#ifndef QT_NO_OPENGL
if (graphicsApi == OpenGL) {
r.fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
QRhiGles2InitParams params;
params.fallbackSurface = r.fallbackSurface;
//params.window = this;
r.r = QRhi::create(QRhi::OpenGLES2, &params);
}
#endif
#if QT_CONFIG(vulkan)
if (graphicsApi == Vulkan) {
QRhiVulkanInitParams params;
params.inst = r.instance;
//params.window = this;
r.r = QRhi::create(QRhi::Vulkan, &params);
}
#endif
#ifdef Q_OS_WIN
if (graphicsApi == D3D11) {
QRhiD3D11InitParams params;
params.enableDebugLayer = true;
r.r = QRhi::create(QRhi::D3D11, &params);
}
#endif
#ifdef Q_OS_DARWIN
if (graphicsApi == Metal) {
QRhiMetalInitParams params;
r.r = QRhi::create(QRhi::Metal, &params);
}
#endif
if (!r.r)
qFatal("Failed to create RHI backend");
}
void destroyRhi()
{
delete r.r;
#ifndef QT_NO_OPENGL
delete r.fallbackSurface;
#endif
}
struct {
QVector<QWindow *> windows;
QRhiBuffer *vbuf = nullptr;
QRhiBuffer *ubuf = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
} d;
static float vertexData[] = {
0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
};
static QShader getShader(const QString &name)
{
QFile f(name);
if (f.open(QIODevice::ReadOnly))
return QShader::fromSerialized(f.readAll());
return QShader();
}
// can use just one rpd from whichever window comes first since they are
// actually compatible due to all windows using the same config (have
// depth-stencil, sample count 1, same format). this means the same pso can be
// reused too.
void ensureSharedResources(QRhiRenderPassDescriptor *rp)
{
if (!d.vbuf) {
d.vbuf = r.r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData));
d.vbuf->build();
d.initialUpdates = r.r->nextResourceUpdateBatch();
d.initialUpdates->uploadStaticBuffer(d.vbuf, vertexData);
}
if (!d.ubuf) {
d.ubuf = r.r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
d.ubuf->build();
}
if (!d.srb) {
d.srb = r.r->newShaderResourceBindings();
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf)
});
d.srb->build();
}
if (!d.ps) {
d.ps = r.r->newGraphicsPipeline();
QRhiGraphicsPipeline::TargetBlend premulAlphaBlend;
premulAlphaBlend.enable = true;
d.ps->setTargetBlends({ premulAlphaBlend });
const QShader vs = getShader(QLatin1String(":/color.vert.qsb"));
if (!vs.isValid())
qFatal("Failed to load shader pack (vertex)");
const QShader fs = getShader(QLatin1String(":/color.frag.qsb"));
if (!fs.isValid())
qFatal("Failed to load shader pack (fragment)");
d.ps->setShaderStages({
{ QRhiGraphicsShaderStage::Vertex, vs },
{ QRhiGraphicsShaderStage::Fragment, fs }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 5 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(rp);
d.ps->build();
}
}
void destroySharedResources()
{
delete d.ps;
d.ps = nullptr;
delete d.srb;
d.srb = nullptr;
delete d.vbuf;
d.vbuf = nullptr;
delete d.ubuf;
d.ubuf = nullptr;
}
class Window : public QWindow
{
public:
Window(const QString &title, const QColor &bgColor, int axis, bool noVSync);
~Window();
protected:
void init();
void releaseResources();
void resizeSwapChain();
void releaseSwapChain();
void render();
void exposeEvent(QExposeEvent *) override;
bool event(QEvent *) override;
QColor m_bgColor;
int m_rotationAxis;
bool m_noVSync;
bool m_running = false;
bool m_notExposed = false;
bool m_newlyExposed = false;
QMatrix4x4 m_proj;
QVector<QRhiResource *> m_releasePool;
bool m_hasSwapChain = false;
QRhiSwapChain *m_sc = nullptr;
QRhiRenderBuffer *m_ds = nullptr;
QRhiRenderPassDescriptor *m_rp = nullptr;
float m_rotation = 0;
float m_opacity = 1;
int m_opacityDir = -1;
};
Window::Window(const QString &title, const QColor &bgColor, int axis, bool noVSync)
: m_bgColor(bgColor),
m_rotationAxis(axis),
m_noVSync(noVSync)
{
switch (graphicsApi) {
case OpenGL:
{
#if QT_CONFIG(opengl)
setSurfaceType(OpenGLSurface);
QSurfaceFormat fmt = QRhiGles2InitParams::adjustedFormat(); // default + depth/stencil
fmt.setSwapInterval(noVSync ? 0 : 1); // + swap interval
setFormat(fmt);
#endif
}
break;
case Vulkan:
#if QT_CONFIG(vulkan)
setSurfaceType(VulkanSurface);
setVulkanInstance(r.instance);
#endif
break;
case D3D11:
setSurfaceType(OpenGLSurface); // not a typo
break;
case Metal:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
setSurfaceType(MetalSurface);
#endif
break;
default:
break;
}
resize(800, 600);
setTitle(title);
}
Window::~Window()
{
releaseResources();
}
void Window::exposeEvent(QExposeEvent *)
{
// initialize and start rendering when the window becomes usable for graphics purposes
if (isExposed() && !m_running) {
m_running = true;
init();
resizeSwapChain();
}
// stop pushing frames when not exposed (or size is 0)
if ((!isExposed() || (m_hasSwapChain && m_sc->surfacePixelSize().isEmpty())) && m_running)
m_notExposed = true;
// continue when exposed again and the surface has a valid size.
// note that the surface size can be (0, 0) even though size() reports a valid one...
if (isExposed() && m_running && m_notExposed && !m_sc->surfacePixelSize().isEmpty()) {
m_notExposed = false;
m_newlyExposed = true;
}
// always render a frame on exposeEvent() (when exposed) in order to update
// immediately on window resize.
if (isExposed() && !m_sc->surfacePixelSize().isEmpty())
render();
}
bool Window::event(QEvent *e)
{
switch (e->type()) {
case QEvent::UpdateRequest:
render();
break;
case QEvent::PlatformSurface:
// this is the proper time to tear down the swapchain (while the native window and surface are still around)
if (static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed)
releaseSwapChain();
break;
default:
break;
}
return QWindow::event(e);
}
void Window::init()
{
m_sc = r.r->newSwapChain();
m_ds = r.r->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
QSize(), // no need to set the size yet
1,
QRhiRenderBuffer::UsedWithSwapChainOnly);
m_releasePool << m_ds;
m_sc->setWindow(this);
m_sc->setDepthStencil(m_ds);
if (m_noVSync)
m_sc->setFlags(QRhiSwapChain::NoVSync);
m_rp = m_sc->newCompatibleRenderPassDescriptor();
m_releasePool << m_rp;
m_sc->setRenderPassDescriptor(m_rp);
ensureSharedResources(m_rp);
}
void Window::releaseResources()
{
qDeleteAll(m_releasePool);
m_releasePool.clear();
delete m_sc;
m_sc = nullptr;
}
void Window::resizeSwapChain()
{
const QSize outputSize = m_sc->surfacePixelSize();
m_ds->setPixelSize(outputSize);
m_ds->build();
m_hasSwapChain = m_sc->buildOrResize();
m_proj = r.r->clipSpaceCorrMatrix();
m_proj.perspective(45.0f, outputSize.width() / (float) outputSize.height(), 0.01f, 1000.0f);
m_proj.translate(0, 0, -4);
}
void Window::releaseSwapChain()
{
if (m_hasSwapChain) {
m_hasSwapChain = false;
m_sc->release();
}
}
void Window::render()
{
if (!m_hasSwapChain || m_notExposed)
return;
// If the window got resized or got newly exposed, resize the swapchain.
// (the newly-exposed case is not actually required by some
// platforms/backends, but f.ex. Vulkan on Windows seems to need it)
if (m_sc->currentPixelSize() != m_sc->surfacePixelSize() || m_newlyExposed) {
resizeSwapChain();
if (!m_hasSwapChain)
return;
m_newlyExposed = false;
}
QRhi::FrameOpResult result = r.r->beginFrame(m_sc);
if (result == QRhi::FrameOpSwapChainOutOfDate) {
resizeSwapChain();
if (!m_hasSwapChain)
return;
result = r.r->beginFrame(m_sc);
}
if (result != QRhi::FrameOpSuccess) {
requestUpdate();
return;
}
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
const QSize outputSizeInPixels = m_sc->currentPixelSize();
QRhiResourceUpdateBatch *u = r.r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
}
m_rotation += 1.0f;
QMatrix4x4 mvp = m_proj;
mvp.rotate(m_rotation, m_rotationAxis == 0 ? 1 : 0, m_rotationAxis == 1 ? 1 : 0, m_rotationAxis == 2 ? 1 : 0);
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
m_opacity += m_opacityDir * 0.005f;
if (m_opacity < 0.0f || m_opacity > 1.0f) {
m_opacityDir *= -1;
m_opacity = qBound(0.0f, m_opacity, 1.0f);
}
u->updateDynamicBuffer(d.ubuf, 64, 4, &m_opacity);
cb->beginPass(m_sc->currentFrameRenderTarget(),
QColor::fromRgbF(float(m_bgColor.redF()), float(m_bgColor.greenF()), float(m_bgColor.blueF()), 1.0f),
{ 1.0f, 0 },
u);
cb->setGraphicsPipeline(d.ps);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(3);
cb->endPass();
r.r->endFrame(m_sc);
requestUpdate();
}
void createWindow(bool noVSync)
{
static QColor colors[] = { Qt::red, Qt::green, Qt::blue, Qt::yellow, Qt::cyan, Qt::gray };
const int n = d.windows.count();
d.windows.append(new Window(QString::asprintf("Window #%d%s", n, noVSync ? " (no vsync)" : ""), colors[n % 6], n % 3, noVSync));
d.windows.last()->show();
}
void closeWindow()
{
delete d.windows.takeLast();
}
int main(int argc, char **argv)
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
#if defined(Q_OS_WIN)
graphicsApi = D3D11;
#elif defined(Q_OS_DARWIN)
graphicsApi = Metal;
#elif QT_CONFIG(vulkan)
graphicsApi = Vulkan;
#else
graphicsApi = OpenGL;
#endif
QCommandLineParser cmdLineParser;
cmdLineParser.addHelpOption();
QCommandLineOption glOption({ "g", "opengl" }, QLatin1String("OpenGL (2.x)"));
cmdLineParser.addOption(glOption);
QCommandLineOption vkOption({ "v", "vulkan" }, QLatin1String("Vulkan"));
cmdLineParser.addOption(vkOption);
QCommandLineOption d3dOption({ "d", "d3d11" }, QLatin1String("Direct3D 11"));
cmdLineParser.addOption(d3dOption);
QCommandLineOption mtlOption({ "m", "metal" }, QLatin1String("Metal"));
cmdLineParser.addOption(mtlOption);
cmdLineParser.process(app);
if (cmdLineParser.isSet(glOption))
graphicsApi = OpenGL;
if (cmdLineParser.isSet(vkOption))
graphicsApi = Vulkan;
if (cmdLineParser.isSet(d3dOption))
graphicsApi = D3D11;
if (cmdLineParser.isSet(mtlOption))
graphicsApi = Metal;
qDebug("Selected graphics API is %s", qPrintable(graphicsApiName()));
qDebug("This is a multi-api example, use command line arguments to override:\n%s", qPrintable(cmdLineParser.helpText()));
#if QT_CONFIG(vulkan)
r.instance = new QVulkanInstance;
if (graphicsApi == Vulkan) {
#ifndef Q_OS_ANDROID
r.instance->setLayers({ "VK_LAYER_LUNARG_standard_validation" });
#else
r.instance->setLayers(QByteArrayList()
<< "VK_LAYER_GOOGLE_threading"
<< "VK_LAYER_LUNARG_parameter_validation"
<< "VK_LAYER_LUNARG_object_tracker"
<< "VK_LAYER_LUNARG_core_validation"
<< "VK_LAYER_LUNARG_image"
<< "VK_LAYER_LUNARG_swapchain"
<< "VK_LAYER_GOOGLE_unique_objects");
#endif
if (!r.instance->create()) {
qWarning("Failed to create Vulkan instance, switching to OpenGL");
graphicsApi = OpenGL;
}
}
#endif
createRhi();
int winCount = 0;
QWidget w;
w.resize(800, 600);
w.setWindowTitle(QCoreApplication::applicationName() + QLatin1String(" - ") + graphicsApiName());
QVBoxLayout *layout = new QVBoxLayout(&w);
QPlainTextEdit *info = new QPlainTextEdit(
QLatin1String("This application tests rendering with the same QRhi instance (and so the same Vulkan/Metal/D3D device or OpenGL context) "
"to multiple windows via multiple QRhiSwapChain objects, from the same one thread. Some resources are shared across all windows.\n"
"\nNote that the behavior may differ depending on the underlying graphics API implementation and the number of windows. "
"One challenge here is the vsync throttling: with the default vsync/fifo presentation mode the behavior may differ between "
"platforms, drivers, and APIs as we present different swapchains' images in a row on the same thread. As a potential solution, "
"setting NoVSync on the second, third, and later window swapchains is offered as an option.\n"
"\n\nUsing API: ") + graphicsApiName());
info->setReadOnly(true);
layout->addWidget(info);
QLabel *label = new QLabel(QLatin1String("Window count: 0"));
layout->addWidget(label);
QCheckBox *vsCb = new QCheckBox(QLatin1String("Set NoVSync on all swapchains except the first"));
vsCb->setChecked(false);
layout->addWidget(vsCb);
QPushButton *btn = new QPushButton(QLatin1String("New window"));
QObject::connect(btn, &QPushButton::clicked, btn, [label, vsCb, &winCount] {
winCount += 1;
label->setText(QString::asprintf("Window count: %d", winCount));
const bool noVSync = vsCb->isChecked() && winCount > 1;
createWindow(noVSync);
});
layout->addWidget(btn);
btn = new QPushButton(QLatin1String("Close window"));
QObject::connect(btn, &QPushButton::clicked, btn, [label, &winCount] {
if (winCount > 0) {
winCount -= 1;
label->setText(QString::asprintf("Window count: %d", winCount));
closeWindow();
}
});
layout->addWidget(btn);
w.show();
int result = app.exec();
qDeleteAll(d.windows);
destroySharedResources();
destroyRhi();
#if QT_CONFIG(vulkan)
delete r.instance;
#endif
return result;
}

View File

@ -0,0 +1,8 @@
TEMPLATE = app
QT += gui-private widgets
SOURCES = \
multiwindow.cpp
RESOURCES = multiwindow.qrc

View File

@ -0,0 +1,6 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="color.vert.qsb">../shared/color.vert.qsb</file>
<file alias="color.frag.qsb">../shared/color.frag.qsb</file>
</qresource>
</RCC>

View File

@ -0,0 +1,829 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QQueue>
#include <QEvent>
#include <QCommandLineParser>
#include <QElapsedTimer>
#include <QtGui/private/qshader_p.h>
#include <QFile>
#include <QtGui/private/qrhiprofiler_p.h>
#ifndef QT_NO_OPENGL
#include <QtGui/private/qrhigles2_p.h>
#include <QOffscreenSurface>
#endif
#if QT_CONFIG(vulkan)
#include <QLoggingCategory>
#include <QtGui/private/qrhivulkan_p.h>
#endif
#ifdef Q_OS_WIN
#include <QtGui/private/qrhid3d11_p.h>
#endif
#ifdef Q_OS_DARWIN
#include <QtGui/private/qrhimetal_p.h>
#endif
#include "window.h"
#include "../shared/cube.h"
static QShader getShader(const QString &name)
{
QFile f(name);
if (f.open(QIODevice::ReadOnly))
return QShader::fromSerialized(f.readAll());
return QShader();
}
static GraphicsApi graphicsApi;
static QString graphicsApiName()
{
switch (graphicsApi) {
case OpenGL:
return QLatin1String("OpenGL 2.x");
case Vulkan:
return QLatin1String("Vulkan");
case D3D11:
return QLatin1String("Direct3D 11");
case Metal:
return QLatin1String("Metal");
default:
break;
}
return QString();
}
#if QT_CONFIG(vulkan)
QVulkanInstance *instance = nullptr;
#endif
// Window (main thread) emit signals -> Renderer::send* (main thread) -> event queue (add on main, process on render thread) -> Renderer::renderEvent (render thread)
// event queue is taken from the Qt Quick scenegraph as-is
// all this below is conceptually the same as the QSG threaded render loop
class RenderThreadEventQueue : public QQueue<QEvent *>
{
public:
RenderThreadEventQueue()
: waiting(false)
{
}
void addEvent(QEvent *e) {
mutex.lock();
enqueue(e);
if (waiting)
condition.wakeOne();
mutex.unlock();
}
QEvent *takeEvent(bool wait) {
mutex.lock();
if (isEmpty() && wait) {
waiting = true;
condition.wait(&mutex);
waiting = false;
}
QEvent *e = dequeue();
mutex.unlock();
return e;
}
bool hasMoreEvents() {
mutex.lock();
bool has = !isEmpty();
mutex.unlock();
return has;
}
private:
QMutex mutex;
QWaitCondition condition;
bool waiting;
};
struct Renderer;
struct Thread : public QThread
{
Thread(Renderer *renderer_)
: renderer(renderer_)
{
active = true;
start();
}
void run() override;
Renderer *renderer;
bool active;
RenderThreadEventQueue eventQueue;
bool sleeping = false;
bool stopEventProcessing = false;
bool pendingRender = false;
bool pendingRenderIsNewExpose = false;
// mutex and cond used to allow the main thread waiting until something completes on the render thread
QMutex mutex;
QWaitCondition cond;
};
class RenderThreadEvent : public QEvent
{
public:
RenderThreadEvent(QEvent::Type type) : QEvent(type) { }
};
class InitEvent : public RenderThreadEvent
{
public:
static const QEvent::Type TYPE = QEvent::Type(QEvent::User + 1);
InitEvent() : RenderThreadEvent(TYPE)
{ }
};
class RequestRenderEvent : public RenderThreadEvent
{
public:
static const QEvent::Type TYPE = QEvent::Type(QEvent::User + 2);
RequestRenderEvent(bool newlyExposed_) : RenderThreadEvent(TYPE), newlyExposed(newlyExposed_)
{ }
bool newlyExposed;
};
class SurfaceCleanupEvent : public RenderThreadEvent
{
public:
static const QEvent::Type TYPE = QEvent::Type(QEvent::User + 3);
SurfaceCleanupEvent() : RenderThreadEvent(TYPE)
{ }
};
class CloseEvent : public RenderThreadEvent
{
public:
static const QEvent::Type TYPE = QEvent::Type(QEvent::User + 4);
CloseEvent() : RenderThreadEvent(TYPE)
{ }
};
class SyncSurfaceSizeEvent : public RenderThreadEvent
{
public:
static const QEvent::Type TYPE = QEvent::Type(QEvent::User + 5);
SyncSurfaceSizeEvent() : RenderThreadEvent(TYPE)
{ }
};
struct Renderer
{
// ctor and dtor and send* are called main thread, rest on the render thread
Renderer(QWindow *w, const QColor &bgColor, int rotationAxis);
~Renderer();
void sendInit();
void sendRender(bool newlyExposed);
void sendSurfaceGoingAway();
void sendSyncSurfaceSize();
QWindow *window;
Thread *thread;
QRhi *r = nullptr;
#ifndef QT_NO_OPENGL
QOffscreenSurface *fallbackSurface = nullptr;
#endif
void createRhi();
void destroyRhi();
void renderEvent(QEvent *e);
void init();
void releaseSwapChain();
void releaseResources();
void render(bool newlyExposed, bool wakeBeforePresent);
QColor m_bgColor;
int m_rotationAxis;
QVector<QRhiResource *> m_releasePool;
bool m_hasSwapChain = false;
QRhiSwapChain *m_sc = nullptr;
QRhiRenderBuffer *m_ds = nullptr;
QRhiRenderPassDescriptor *m_rp = nullptr;
QRhiBuffer *m_vbuf = nullptr;
QRhiBuffer *m_ubuf = nullptr;
QRhiTexture *m_tex = nullptr;
QRhiSampler *m_sampler = nullptr;
QRhiShaderResourceBindings *m_srb = nullptr;
QRhiGraphicsPipeline *m_ps = nullptr;
QRhiResourceUpdateBatch *m_initialUpdates = nullptr;
QMatrix4x4 m_proj;
float m_rotation = 0;
int m_frameCount = 0;
};
void Thread::run()
{
while (active) {
if (pendingRender) {
pendingRender = false;
renderer->render(pendingRenderIsNewExpose, false);
}
while (eventQueue.hasMoreEvents()) {
QEvent *e = eventQueue.takeEvent(false);
renderer->renderEvent(e);
delete e;
}
if (active && !pendingRender) {
sleeping = true;
stopEventProcessing = false;
while (!stopEventProcessing) {
QEvent *e = eventQueue.takeEvent(true);
renderer->renderEvent(e);
delete e;
}
sleeping = false;
}
}
}
Renderer::Renderer(QWindow *w, const QColor &bgColor, int rotationAxis)
: window(w),
m_bgColor(bgColor),
m_rotationAxis(rotationAxis)
{ // main thread
thread = new Thread(this);
#ifndef QT_NO_OPENGL
if (graphicsApi == OpenGL)
fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
#endif
}
Renderer::~Renderer()
{ // main thread
thread->eventQueue.addEvent(new CloseEvent);
thread->wait();
delete thread;
#ifndef QT_NO_OPENGL
delete fallbackSurface;
#endif
}
void Renderer::createRhi()
{
if (r)
return;
qDebug() << "renderer" << this << "creating rhi";
QRhi::Flags rhiFlags = QRhi::EnableProfiling;
#ifndef QT_NO_OPENGL
if (graphicsApi == OpenGL) {
QRhiGles2InitParams params;
params.fallbackSurface = fallbackSurface;
params.window = window;
r = QRhi::create(QRhi::OpenGLES2, &params, rhiFlags);
}
#endif
#if QT_CONFIG(vulkan)
if (graphicsApi == Vulkan) {
QRhiVulkanInitParams params;
params.inst = instance;
params.window = window;
r = QRhi::create(QRhi::Vulkan, &params, rhiFlags);
}
#endif
#ifdef Q_OS_WIN
if (graphicsApi == D3D11) {
QRhiD3D11InitParams params;
params.enableDebugLayer = true;
r = QRhi::create(QRhi::D3D11, &params, rhiFlags);
}
#endif
#ifdef Q_OS_DARWIN
if (graphicsApi == Metal) {
QRhiMetalInitParams params;
r = QRhi::create(QRhi::Metal, &params, rhiFlags);
}
#endif
if (!r)
qFatal("Failed to create RHI backend");
}
void Renderer::destroyRhi()
{
qDebug() << "renderer" << this << "destroying rhi";
delete r;
r = nullptr;
}
void Renderer::renderEvent(QEvent *e)
{
Q_ASSERT(QThread::currentThread() == thread);
if (thread->sleeping)
thread->stopEventProcessing = true;
switch (int(e->type())) {
case InitEvent::TYPE:
qDebug() << "renderer" << this << "for window" << window << "is initializing";
createRhi();
init();
break;
case RequestRenderEvent::TYPE:
thread->pendingRender = true;
thread->pendingRenderIsNewExpose = static_cast<RequestRenderEvent *>(e)->newlyExposed;
break;
case SurfaceCleanupEvent::TYPE: // when the QWindow is closed, before QPlatformWindow goes away
thread->mutex.lock();
qDebug() << "renderer" << this << "for window" << window << "is destroying swapchain";
thread->pendingRender = false;
releaseSwapChain();
thread->cond.wakeOne();
thread->mutex.unlock();
break;
case CloseEvent::TYPE: // when destroying the window+renderer (NB not the same as hitting X on the window, that's just QWindow close)
qDebug() << "renderer" << this << "for window" << window << "is shutting down";
thread->pendingRender = false;
thread->active = false;
thread->stopEventProcessing = true;
releaseResources();
destroyRhi();
break;
case SyncSurfaceSizeEvent::TYPE:
thread->mutex.lock();
thread->pendingRender = false;
render(false, true);
break;
default:
break;
}
}
void Renderer::init()
{
m_sc = r->newSwapChain();
m_ds = r->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
QSize(), // no need to set the size yet
1,
QRhiRenderBuffer::UsedWithSwapChainOnly);
m_releasePool << m_ds;
m_sc->setWindow(window);
m_sc->setDepthStencil(m_ds);
m_rp = m_sc->newCompatibleRenderPassDescriptor();
m_releasePool << m_rp;
m_sc->setRenderPassDescriptor(m_rp);
m_vbuf = r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(cube));
m_releasePool << m_vbuf;
m_vbuf->build();
m_ubuf = r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64 + 4);
m_releasePool << m_ubuf;
m_ubuf->build();
QImage image = QImage(QLatin1String(":/qt256.png")).convertToFormat(QImage::Format_RGBA8888);
m_tex = r->newTexture(QRhiTexture::RGBA8, image.size());
m_releasePool << m_tex;
m_tex->build();
m_sampler = r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
m_releasePool << m_sampler;
m_sampler->build();
m_srb = r->newShaderResourceBindings();
m_releasePool << m_srb;
m_srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, m_ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, m_tex, m_sampler)
});
m_srb->build();
m_ps = r->newGraphicsPipeline();
m_releasePool << m_ps;
m_ps->setDepthTest(true);
m_ps->setDepthWrite(true);
m_ps->setDepthOp(QRhiGraphicsPipeline::Less);
m_ps->setCullMode(QRhiGraphicsPipeline::Back);
m_ps->setFrontFace(QRhiGraphicsPipeline::CCW);
m_ps->setShaderStages({
{ QRhiGraphicsShaderStage::Vertex, getShader(QLatin1String(":/texture.vert.qsb")) },
{ QRhiGraphicsShaderStage::Fragment, getShader(QLatin1String(":/texture.frag.qsb")) }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 3 * sizeof(float) },
{ 2 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float3, 0 },
{ 1, 1, QRhiVertexInputAttribute::Float2, 0 }
});
m_ps->setVertexInputLayout(inputLayout);
m_ps->setShaderResourceBindings(m_srb);
m_ps->setRenderPassDescriptor(m_rp);
m_ps->build();
m_initialUpdates = r->nextResourceUpdateBatch();
m_initialUpdates->uploadStaticBuffer(m_vbuf, cube);
qint32 flip = 0;
m_initialUpdates->updateDynamicBuffer(m_ubuf, 64, 4, &flip);
m_initialUpdates->uploadTexture(m_tex, image);
}
void Renderer::releaseSwapChain()
{
if (m_hasSwapChain) {
m_hasSwapChain = false;
m_sc->release();
}
}
void Renderer::releaseResources()
{
qDeleteAll(m_releasePool);
m_releasePool.clear();
delete m_sc;
m_sc = nullptr;
}
void Renderer::render(bool newlyExposed, bool wakeBeforePresent)
{
// This function handles both resizing and rendering. Resizes have some
// complications due to the threaded model (check exposeEvent and
// sendSyncSurfaceSize) but don't have to worry about that in here.
auto buildOrResizeSwapChain = [this] {
qDebug() << "renderer" << this << "build or resize swapchain for window" << window;
const QSize outputSize = m_sc->surfacePixelSize();
qDebug() << " size is" << outputSize;
m_ds->setPixelSize(outputSize);
m_ds->build();
m_hasSwapChain = m_sc->buildOrResize();
m_proj = r->clipSpaceCorrMatrix();
m_proj.perspective(45.0f, outputSize.width() / (float) outputSize.height(), 0.01f, 100.0f);
m_proj.translate(0, 0, -4);
};
auto wakeUpIfNeeded = [wakeBeforePresent, this] {
// make sure the main/gui thread is not blocked when issuing the Present (or equivalent)
if (wakeBeforePresent) {
thread->cond.wakeOne();
thread->mutex.unlock();
}
};
const QSize surfaceSize = m_sc->surfacePixelSize();
if (surfaceSize.isEmpty()) {
wakeUpIfNeeded();
return;
}
if (newlyExposed || m_sc->currentPixelSize() != surfaceSize)
buildOrResizeSwapChain();
if (!m_hasSwapChain) {
wakeUpIfNeeded();
return;
}
QRhi::FrameOpResult result = r->beginFrame(m_sc);
if (result == QRhi::FrameOpSwapChainOutOfDate) {
buildOrResizeSwapChain();
if (!m_hasSwapChain) {
wakeUpIfNeeded();
return;
}
result = r->beginFrame(m_sc);
}
if (result != QRhi::FrameOpSuccess) {
wakeUpIfNeeded();
return;
}
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
const QSize outputSize = m_sc->currentPixelSize();
QRhiResourceUpdateBatch *u = r->nextResourceUpdateBatch();
if (m_initialUpdates) {
u->merge(m_initialUpdates);
m_initialUpdates->release();
m_initialUpdates = nullptr;
}
m_rotation += 1.0f;
QMatrix4x4 mvp = m_proj;
mvp.scale(0.5f);
mvp.rotate(m_rotation, m_rotationAxis == 0 ? 1 : 0, m_rotationAxis == 1 ? 1 : 0, m_rotationAxis == 2 ? 1 : 0);
u->updateDynamicBuffer(m_ubuf, 0, 64, mvp.constData());
cb->beginPass(m_sc->currentFrameRenderTarget(),
QColor::fromRgbF(float(m_bgColor.redF()), float(m_bgColor.greenF()), float(m_bgColor.blueF()), 1.0f),
{ 1.0f, 0 },
u);
cb->setGraphicsPipeline(m_ps);
cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height()));
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBindings[] = {
{ m_vbuf, 0 },
{ m_vbuf, 36 * 3 * sizeof(float) }
};
cb->setVertexInput(0, 2, vbufBindings);
cb->draw(36);
cb->endPass();
wakeUpIfNeeded();
r->endFrame(m_sc);
m_frameCount += 1;
if ((m_frameCount % 300) == 0) {
const QRhiProfiler::CpuTime ff = r->profiler()->frameToFrameTimes(m_sc);
const QRhiProfiler::CpuTime be = r->profiler()->frameBuildTimes(m_sc);
const QRhiProfiler::GpuTime gp = r->profiler()->gpuFrameTimes(m_sc);
if (r->isFeatureSupported(QRhi::Timestamps)) {
qDebug("[renderer %p] frame-to-frame: min %lld max %lld avg %f. "
"frame build: min %lld max %lld avg %f. "
"gpu frame time: min %f max %f avg %f",
this,
ff.minTime, ff.maxTime, ff.avgTime,
be.minTime, be.maxTime, be.avgTime,
gp.minTime, gp.maxTime, gp.avgTime);
} else {
qDebug("[renderer %p] frame-to-frame: min %lld max %lld avg %f. "
"frame build: min %lld max %lld avg %f. ",
this,
ff.minTime, ff.maxTime, ff.avgTime,
be.minTime, be.maxTime, be.avgTime);
}
}
}
void Renderer::sendInit()
{ // main thread
InitEvent *e = new InitEvent;
thread->eventQueue.addEvent(e);
}
void Renderer::sendRender(bool newlyExposed)
{ // main thread
RequestRenderEvent *e = new RequestRenderEvent(newlyExposed);
thread->eventQueue.addEvent(e);
}
void Renderer::sendSurfaceGoingAway()
{ // main thread
SurfaceCleanupEvent *e = new SurfaceCleanupEvent;
// cannot let this thread to proceed with tearing down the native window
// before the render thread completes the swapchain release
thread->mutex.lock();
thread->eventQueue.addEvent(e);
thread->cond.wait(&thread->mutex);
thread->mutex.unlock();
}
void Renderer::sendSyncSurfaceSize()
{ // main thread
SyncSurfaceSizeEvent *e = new SyncSurfaceSizeEvent;
// must block to prevent surface size mess. the render thread will do a
// full rendering round before it unlocks which is good since it can thus
// pick up and the surface (window) size atomically.
thread->mutex.lock();
thread->eventQueue.addEvent(e);
thread->cond.wait(&thread->mutex);
thread->mutex.unlock();
}
struct WindowAndRenderer
{
QWindow *window;
Renderer *renderer;
};
QVector<WindowAndRenderer> windows;
void createWindow()
{
static QColor colors[] = { Qt::red, Qt::green, Qt::blue, Qt::yellow, Qt::cyan, Qt::gray };
const int n = windows.count();
Window *w = new Window(QString::asprintf("Window+Thread #%d (%s)", n, qPrintable(graphicsApiName())), graphicsApi);
Renderer *renderer = new Renderer(w, colors[n % 6], n % 3);;
QObject::connect(w, &Window::initRequested, w, [renderer] {
renderer->sendInit();
});
QObject::connect(w, &Window::renderRequested, w, [w, renderer](bool newlyExposed) {
renderer->sendRender(newlyExposed);
w->requestUpdate();
});
QObject::connect(w, &Window::surfaceGoingAway, w, [renderer] {
renderer->sendSurfaceGoingAway();
});
QObject::connect(w, &Window::syncSurfaceSizeRequested, w, [renderer] {
renderer->sendSyncSurfaceSize();
});
windows.append({ w, renderer });
w->show();
}
void closeWindow()
{
WindowAndRenderer wr = windows.takeLast();
delete wr.renderer;
delete wr.window;
}
int main(int argc, char **argv)
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
#if defined(Q_OS_WIN)
graphicsApi = D3D11;
#elif defined(Q_OS_DARWIN)
graphicsApi = Metal;
#elif QT_CONFIG(vulkan)
graphicsApi = Vulkan;
#else
graphicsApi = OpenGL;
#endif
QCommandLineParser cmdLineParser;
cmdLineParser.addHelpOption();
QCommandLineOption glOption({ "g", "opengl" }, QLatin1String("OpenGL (2.x)"));
cmdLineParser.addOption(glOption);
QCommandLineOption vkOption({ "v", "vulkan" }, QLatin1String("Vulkan"));
cmdLineParser.addOption(vkOption);
QCommandLineOption d3dOption({ "d", "d3d11" }, QLatin1String("Direct3D 11"));
cmdLineParser.addOption(d3dOption);
QCommandLineOption mtlOption({ "m", "metal" }, QLatin1String("Metal"));
cmdLineParser.addOption(mtlOption);
cmdLineParser.process(app);
if (cmdLineParser.isSet(glOption))
graphicsApi = OpenGL;
if (cmdLineParser.isSet(vkOption))
graphicsApi = Vulkan;
if (cmdLineParser.isSet(d3dOption))
graphicsApi = D3D11;
if (cmdLineParser.isSet(mtlOption))
graphicsApi = Metal;
qDebug("Selected graphics API is %s", qPrintable(graphicsApiName()));
qDebug("This is a multi-api example, use command line arguments to override:\n%s", qPrintable(cmdLineParser.helpText()));
#if QT_CONFIG(vulkan)
instance = new QVulkanInstance;
if (graphicsApi == Vulkan) {
#ifndef Q_OS_ANDROID
instance->setLayers({ "VK_LAYER_LUNARG_standard_validation" });
#else
instance->setLayers(QByteArrayList()
<< "VK_LAYER_GOOGLE_threading"
<< "VK_LAYER_LUNARG_parameter_validation"
<< "VK_LAYER_LUNARG_object_tracker"
<< "VK_LAYER_LUNARG_core_validation"
<< "VK_LAYER_LUNARG_image"
<< "VK_LAYER_LUNARG_swapchain"
<< "VK_LAYER_GOOGLE_unique_objects");
#endif
if (!instance->create()) {
qWarning("Failed to create Vulkan instance, switching to OpenGL");
graphicsApi = OpenGL;
}
}
#endif
int winCount = 0;
QWidget w;
w.resize(800, 600);
w.setWindowTitle(QCoreApplication::applicationName() + QLatin1String(" - ") + graphicsApiName());
QVBoxLayout *layout = new QVBoxLayout(&w);
QPlainTextEdit *info = new QPlainTextEdit(
QLatin1String("This application tests rendering on a separate thread per window, with dedicated QRhi instances and resources. "
"\n\nThis is the same concept as the Qt Quick Scenegraph's threaded render loop. This should allow rendering to the different windows "
"without unintentionally throttling each other's threads."
"\n\nUsing API: ") + graphicsApiName());
info->setReadOnly(true);
layout->addWidget(info);
QLabel *label = new QLabel(QLatin1String("Window and thread count: 0"));
layout->addWidget(label);
QPushButton *btn = new QPushButton(QLatin1String("New window"));
QObject::connect(btn, &QPushButton::clicked, btn, [label, &winCount] {
winCount += 1;
label->setText(QString::asprintf("Window count: %d", winCount));
createWindow();
});
layout->addWidget(btn);
btn = new QPushButton(QLatin1String("Close window"));
QObject::connect(btn, &QPushButton::clicked, btn, [label, &winCount] {
if (winCount > 0) {
winCount -= 1;
label->setText(QString::asprintf("Window count: %d", winCount));
closeWindow();
}
});
layout->addWidget(btn);
w.show();
int result = app.exec();
for (const WindowAndRenderer &wr : windows) {
delete wr.renderer;
delete wr.window;
}
#if QT_CONFIG(vulkan)
delete instance;
#endif
return result;
}

View File

@ -0,0 +1,12 @@
TEMPLATE = app
QT += gui-private widgets
SOURCES = \
multiwindow_threaded.cpp \
window.cpp
HEADERS = \
window.h
RESOURCES = multiwindow_threaded.qrc

View File

@ -0,0 +1,7 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="qt256.png">../shared/qt256.png</file>
<file alias="texture.vert.qsb">../shared/texture.vert.qsb</file>
<file alias="texture.frag.qsb">../shared/texture.frag.qsb</file>
</qresource>
</RCC>

View File

@ -0,0 +1,142 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "window.h"
#include <QPlatformSurfaceEvent>
#ifndef QT_NO_OPENGL
#include <QtGui/private/qrhigles2_p.h>
#endif
#if QT_CONFIG(vulkan)
extern QVulkanInstance *instance;
#endif
Window::Window(const QString &title, GraphicsApi api)
{
switch (api) {
case OpenGL:
#if QT_CONFIG(opengl)
setSurfaceType(OpenGLSurface);
setFormat(QRhiGles2InitParams::adjustedFormat());
#endif
break;
case Vulkan:
#if QT_CONFIG(vulkan)
setSurfaceType(VulkanSurface);
setVulkanInstance(instance);
#endif
break;
case D3D11:
setSurfaceType(OpenGLSurface); // not a typo
break;
case Metal:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
setSurfaceType(MetalSurface);
#endif
break;
default:
break;
}
resize(800, 600);
setTitle(title);
}
Window::~Window()
{
}
void Window::exposeEvent(QExposeEvent *)
{
if (isExposed()) {
if (!m_running) {
// initialize and start rendering when the window becomes usable for graphics purposes
m_running = true;
m_notExposed = false;
emit initRequested();
emit renderRequested(true);
} else {
// continue when exposed again
if (m_notExposed) {
m_notExposed = false;
emit renderRequested(true);
} else {
// resize generates exposes - this is very important here (unlike in a single-threaded renderer)
emit syncSurfaceSizeRequested();
}
}
} else {
// stop pushing frames when not exposed (on some platforms this is essential, optional on others)
if (m_running)
m_notExposed = true;
}
}
bool Window::event(QEvent *e)
{
switch (e->type()) {
case QEvent::UpdateRequest:
if (!m_notExposed)
emit renderRequested(false);
break;
case QEvent::PlatformSurface:
// this is the proper time to tear down the swapchain (while the native window and surface are still around)
if (static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed)
emit surfaceGoingAway();
break;
default:
break;
}
return QWindow::event(e);
}

View File

@ -0,0 +1,86 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef WINDOW_H
#define WINDOW_H
#include <QWindow>
enum GraphicsApi
{
OpenGL,
Vulkan,
D3D11,
Metal
};
class Window : public QWindow
{
Q_OBJECT
public:
Window(const QString &title, GraphicsApi api);
~Window();
void exposeEvent(QExposeEvent *) override;
bool event(QEvent *) override;
signals:
void initRequested();
void renderRequested(bool newlyExposed);
void surfaceGoingAway();
void syncSurfaceSizeRequested();
protected:
bool m_running = false;
bool m_notExposed = true;
};
#endif

View File

@ -0,0 +1,366 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QGuiApplication>
#include <QImage>
#include <QFileInfo>
#include <QFile>
#include <QLoggingCategory>
#include <QCommandLineParser>
#include <QtGui/private/qshader_p.h>
#include <QtGui/private/qrhinull_p.h>
#ifndef QT_NO_OPENGL
#include <QtGui/private/qrhigles2_p.h>
#include <QOffscreenSurface>
#endif
#if QT_CONFIG(vulkan)
#include <QLoggingCategory>
#include <QtGui/private/qrhivulkan_p.h>
#endif
#ifdef Q_OS_WIN
#include <QtGui/private/qrhid3d11_p.h>
#endif
#ifdef Q_OS_DARWIN
#include <QtGui/private/qrhimetal_p.h>
#endif
//#define TEST_FINISH
static float vertexData[] = { // Y up (note m_proj), CCW
0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
};
static QShader getShader(const QString &name)
{
QFile f(name);
if (f.open(QIODevice::ReadOnly))
return QShader::fromSerialized(f.readAll());
return QShader();
}
enum GraphicsApi
{
OpenGL,
Vulkan,
D3D11,
Metal,
Null
};
GraphicsApi graphicsApi;
QString graphicsApiName()
{
switch (graphicsApi) {
case OpenGL:
return QLatin1String("OpenGL 2.x");
case Vulkan:
return QLatin1String("Vulkan");
case D3D11:
return QLatin1String("Direct3D 11");
case Metal:
return QLatin1String("Metal");
case Null:
return QLatin1String("Null");
default:
break;
}
return QString();
}
int main(int argc, char **argv)
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
#if defined(Q_OS_WIN)
graphicsApi = D3D11;
#elif defined(Q_OS_DARWIN)
graphicsApi = Metal;
#elif QT_CONFIG(vulkan)
graphicsApi = Vulkan;
#else
graphicsApi = OpenGL;
#endif
QCommandLineParser cmdLineParser;
cmdLineParser.addHelpOption();
QCommandLineOption glOption({ "g", "opengl" }, QLatin1String("OpenGL (2.x)"));
cmdLineParser.addOption(glOption);
QCommandLineOption vkOption({ "v", "vulkan" }, QLatin1String("Vulkan"));
cmdLineParser.addOption(vkOption);
QCommandLineOption d3dOption({ "d", "d3d11" }, QLatin1String("Direct3D 11"));
cmdLineParser.addOption(d3dOption);
QCommandLineOption mtlOption({ "m", "metal" }, QLatin1String("Metal"));
cmdLineParser.addOption(mtlOption);
QCommandLineOption nullOption({ "n", "null" }, QLatin1String("Null"));
cmdLineParser.addOption(nullOption);
cmdLineParser.process(app);
if (cmdLineParser.isSet(glOption))
graphicsApi = OpenGL;
if (cmdLineParser.isSet(vkOption))
graphicsApi = Vulkan;
if (cmdLineParser.isSet(d3dOption))
graphicsApi = D3D11;
if (cmdLineParser.isSet(mtlOption))
graphicsApi = Metal;
if (cmdLineParser.isSet(nullOption))
graphicsApi = Null;
qDebug("Selected graphics API is %s", qPrintable(graphicsApiName()));
qDebug("This is a multi-api example, use command line arguments to override:\n%s", qPrintable(cmdLineParser.helpText()));
QRhi *r = nullptr;
if (graphicsApi == Null) {
QRhiNullInitParams params;
r = QRhi::create(QRhi::Null, &params);
}
#if QT_CONFIG(vulkan)
QVulkanInstance inst;
if (graphicsApi == Vulkan) {
QLoggingCategory::setFilterRules(QStringLiteral("qt.vulkan=true"));
#ifndef Q_OS_ANDROID
inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
#else
inst.setLayers(QByteArrayList()
<< "VK_LAYER_GOOGLE_threading"
<< "VK_LAYER_LUNARG_parameter_validation"
<< "VK_LAYER_LUNARG_object_tracker"
<< "VK_LAYER_LUNARG_core_validation"
<< "VK_LAYER_LUNARG_image"
<< "VK_LAYER_LUNARG_swapchain"
<< "VK_LAYER_GOOGLE_unique_objects");
#endif
if (inst.create()) {
QRhiVulkanInitParams params;
params.inst = &inst;
r = QRhi::create(QRhi::Vulkan, &params);
} else {
qWarning("Failed to create Vulkan instance, switching to OpenGL");
graphicsApi = OpenGL;
}
}
#endif
#ifndef QT_NO_OPENGL
QScopedPointer<QOffscreenSurface> offscreenSurface;
if (graphicsApi == OpenGL) {
offscreenSurface.reset(QRhiGles2InitParams::newFallbackSurface());
QRhiGles2InitParams params;
params.fallbackSurface = offscreenSurface.data();
r = QRhi::create(QRhi::OpenGLES2, &params);
}
#endif
#ifdef Q_OS_WIN
if (graphicsApi == D3D11) {
QRhiD3D11InitParams params;
params.enableDebugLayer = true;
r = QRhi::create(QRhi::D3D11, &params);
}
#endif
#ifdef Q_OS_DARWIN
if (graphicsApi == Metal) {
QRhiMetalInitParams params;
r = QRhi::create(QRhi::Metal, &params);
}
#endif
if (!r)
qFatal("Failed to initialize RHI");
QRhiTexture *tex = r->newTexture(QRhiTexture::RGBA8, QSize(1280, 720), 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
tex->build();
QRhiTextureRenderTarget *rt = r->newTextureRenderTarget({ tex });
QRhiRenderPassDescriptor *rp = rt->newCompatibleRenderPassDescriptor();
rt->setRenderPassDescriptor(rp);
rt->build();
QMatrix4x4 proj = r->clipSpaceCorrMatrix();
proj.perspective(45.0f, 1280 / 720.f, 0.01f, 1000.0f);
proj.translate(0, 0, -4);
QRhiBuffer *vbuf = r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData));
vbuf->build();
QRhiBuffer *ubuf = r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
ubuf->build();
QRhiShaderResourceBindings *srb = r->newShaderResourceBindings();
srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, ubuf)
});
srb->build();
QRhiGraphicsPipeline *ps = r->newGraphicsPipeline();
QRhiGraphicsPipeline::TargetBlend premulAlphaBlend;
premulAlphaBlend.enable = true;
ps->setTargetBlends({ premulAlphaBlend });
const QShader vs = getShader(QLatin1String(":/color.vert.qsb"));
if (!vs.isValid())
qFatal("Failed to load shader pack (vertex)");
const QShader fs = getShader(QLatin1String(":/color.frag.qsb"));
if (!fs.isValid())
qFatal("Failed to load shader pack (fragment)");
ps->setShaderStages({
{ QRhiGraphicsShaderStage::Vertex, vs },
{ QRhiGraphicsShaderStage::Fragment, fs }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 5 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) }
});
ps->setVertexInputLayout(inputLayout);
ps->setShaderResourceBindings(srb);
ps->setRenderPassDescriptor(rp);
ps->build();
int frame = 0;
for (; frame < 20; ++frame) {
QRhiCommandBuffer *cb;
if (r->beginOffscreenFrame(&cb) != QRhi::FrameOpSuccess)
break;
qDebug("Generating offscreen frame %d", frame);
QRhiResourceUpdateBatch *u = r->nextResourceUpdateBatch();
if (frame == 0)
u->uploadStaticBuffer(vbuf, vertexData);
static float rotation = 0.0f;
QMatrix4x4 mvp = proj;
mvp.rotate(rotation, 0, 1, 0);
u->updateDynamicBuffer(ubuf, 0, 64, mvp.constData());
rotation += 5.0f;
static float opacity = 1.0f;
static int opacityDir= 1;
u->updateDynamicBuffer(ubuf, 64, 4, &opacity);
opacity += opacityDir * 0.005f;
if (opacity < 0.0f || opacity > 1.0f) {
opacityDir *= -1;
opacity = qBound(0.0f, opacity, 1.0f);
}
cb->beginPass(rt, { 0, 1, 0, 1 }, { 1, 0 }, u);
cb->setGraphicsPipeline(ps);
cb->setViewport({ 0, 0, 1280, 720 });
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBinding(vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(3);
u = r->nextResourceUpdateBatch();
QRhiReadbackDescription rb(tex);
QRhiReadbackResult rbResult;
rbResult.completed = [frame] { qDebug(" - readback %d completed", frame); };
u->readBackTexture(rb, &rbResult);
cb->endPass(u);
qDebug("Submit and wait");
#ifdef TEST_FINISH
r->finish();
#else
r->endOffscreenFrame();
#endif
// The data should be ready either because endOffscreenFrame() waits
// for completion or because finish() did.
if (!rbResult.data.isEmpty()) {
const uchar *p = reinterpret_cast<const uchar *>(rbResult.data.constData());
QImage image(p, rbResult.pixelSize.width(), rbResult.pixelSize.height(), QImage::Format_RGBA8888);
QString fn = QString::asprintf("frame%d.png", frame);
fn = QFileInfo(fn).absoluteFilePath();
qDebug("Saving into %s", qPrintable(fn));
if (r->isYUpInFramebuffer())
image.mirrored().save(fn);
else
image.save(fn);
} else {
qWarning("Readback failed!");
}
#ifdef TEST_FINISH
r->endOffscreenFrame();
#endif
}
delete ps;
delete srb;
delete ubuf;
delete vbuf;
delete rt;
delete rp;
delete tex;
delete r;
qDebug("\nRendered and read back %d frames using %s", frame, qPrintable(graphicsApiName()));
return 0;
}

View File

@ -0,0 +1,9 @@
TEMPLATE = app
CONFIG += console
QT += gui-private
SOURCES = \
offscreen.cpp
RESOURCES = offscreen.qrc

View File

@ -0,0 +1,6 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="color.vert.qsb">../shared/color.vert.qsb</file>
<file alias="color.frag.qsb">../shared/color.frag.qsb</file>
</qresource>
</RCC>

View File

@ -0,0 +1,671 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
// Simple QRhiProfiler receiver app. Start it and then in a QRhi-based
// application connect with a QTcpSocket to 127.0.0.1:30667 and set that as the
// QRhiProfiler's device.
#include <QTcpServer>
#include <QTcpSocket>
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QTextEdit>
#include <QLabel>
#include <QTime>
#include <QtGui/private/qrhiprofiler_p.h>
const int MIN_KNOWN_OP = 1;
const int MAX_KNOWN_OP = 18;
class Parser : public QObject
{
Q_OBJECT
public:
void feed(const QByteArray &line);
struct Event {
QRhiProfiler::StreamOp op;
qint64 timestamp;
quint64 resource;
QByteArray resourceName;
struct Param {
enum ValueType {
Int64,
Float
};
QByteArray key;
ValueType valueType;
union {
qint64 intValue;
float floatValue;
};
};
QVector<Param> params;
const Param *param(const char *key) const {
auto it = std::find_if(params.cbegin(), params.cend(), [key](const Param &p) {
return !strcmp(p.key.constData(), key);
});
return it == params.cend() ? nullptr : &*it;
}
};
signals:
void eventReceived(const Event &e);
};
void Parser::feed(const QByteArray &line)
{
const QList<QByteArray> elems = line.split(',');
if (elems.count() < 4) {
qWarning("Malformed line '%s'", line.constData());
return;
}
bool ok = false;
const int op = elems[0].toInt(&ok);
if (!ok) {
qWarning("Invalid op %s", elems[0].constData());
return;
}
if (op < MIN_KNOWN_OP || op > MAX_KNOWN_OP) {
qWarning("Unknown op %d", op);
return;
}
Event e;
e.op = QRhiProfiler::StreamOp(op);
e.timestamp = elems[1].toLongLong();
e.resource = elems[2].toULongLong();
e.resourceName = elems[3];
const int elemCount = elems.count();
for (int i = 4; i < elemCount; i += 2) {
if (i + 1 < elemCount && !elems[i].isEmpty() && !elems[i + 1].isEmpty()) {
QByteArray key = elems[i];
if (key.startsWith('F')) {
key = key.mid(1);
bool ok = false;
const float value = elems[i + 1].toFloat(&ok);
if (!ok) {
qWarning("Failed to parse float %s in line '%s'", elems[i + 1].constData(), line.constData());
continue;
}
Event::Param param;
param.key = key;
param.valueType = Event::Param::Float;
param.floatValue = value;
e.params.append(param);
} else {
const qint64 value = elems[i + 1].toLongLong();
Event::Param param;
param.key = key;
param.valueType = Event::Param::Int64;
param.intValue = value;
e.params.append(param);
}
}
}
emit eventReceived(e);
}
class Tracker : public QObject
{
Q_OBJECT
public slots:
void handleEvent(const Parser::Event &e);
signals:
void buffersTouched();
void texturesTouched();
void swapchainsTouched();
void frameTimeTouched();
void gpuFrameTimeTouched();
void gpuMemAllocStatsTouched();
public:
Tracker() {
reset();
}
static const int MAX_STAGING_SLOTS = 3;
struct Buffer {
Buffer()
{
memset(stagingExtraSize, 0, sizeof(stagingExtraSize));
}
quint64 lastTimestamp;
QByteArray resourceName;
qint64 effectiveSize = 0;
int backingGpuBufCount = 1;
qint64 stagingExtraSize[MAX_STAGING_SLOTS];
};
QHash<qint64, Buffer> m_buffers;
qint64 m_totalBufferApproxByteSize;
qint64 m_peakBufferApproxByteSize;
qint64 m_totalStagingBufferApproxByteSize;
qint64 m_peakStagingBufferApproxByteSize;
struct Texture {
Texture()
{
memset(stagingExtraSize, 0, sizeof(stagingExtraSize));
}
quint64 lastTimestamp;
QByteArray resourceName;
qint64 approxByteSize = 0;
bool ownsNativeResource = true;
qint64 stagingExtraSize[MAX_STAGING_SLOTS];
};
QHash<qint64, Texture> m_textures;
qint64 m_totalTextureApproxByteSize;
qint64 m_peakTextureApproxByteSize;
qint64 m_totalTextureStagingBufferApproxByteSize;
qint64 m_peakTextureStagingBufferApproxByteSize;
struct SwapChain {
quint64 lastTimestamp;
QByteArray resourceName;
qint64 approxByteSize = 0;
};
QHash<qint64, SwapChain> m_swapchains;
qint64 m_totalSwapChainApproxByteSize;
qint64 m_peakSwapChainApproxByteSize;
struct FrameTime {
qint64 framesSinceResize = 0;
int minDelta = 0;
int maxDelta = 0;
float avgDelta = 0;
};
FrameTime m_lastFrameTime;
struct GpuFrameTime {
float minTime = 0;
float maxTime = 0;
float avgTime = 0;
};
GpuFrameTime m_lastGpuFrameTime;
struct GpuMemAllocStats {
qint64 realAllocCount;
qint64 subAllocCount;
qint64 totalSize;
qint64 unusedSize;
};
GpuMemAllocStats m_lastGpuMemAllocStats;
void reset() {
m_buffers.clear();
m_textures.clear();
m_totalBufferApproxByteSize = 0;
m_peakBufferApproxByteSize = 0;
m_totalStagingBufferApproxByteSize = 0;
m_peakStagingBufferApproxByteSize = 0;
m_totalTextureApproxByteSize = 0;
m_peakTextureApproxByteSize = 0;
m_totalTextureStagingBufferApproxByteSize = 0;
m_peakTextureStagingBufferApproxByteSize = 0;
m_totalSwapChainApproxByteSize = 0;
m_peakSwapChainApproxByteSize = 0;
m_lastFrameTime = FrameTime();
m_lastGpuFrameTime = GpuFrameTime();
m_lastGpuMemAllocStats = GpuMemAllocStats();
emit buffersTouched();
emit texturesTouched();
emit swapchainsTouched();
emit frameTimeTouched();
emit gpuFrameTimeTouched();
emit gpuMemAllocStatsTouched();
}
};
Q_DECLARE_TYPEINFO(Tracker::Buffer, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(Tracker::Texture, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(Tracker::FrameTime, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(Tracker::GpuFrameTime, Q_MOVABLE_TYPE);
void Tracker::handleEvent(const Parser::Event &e)
{
switch (e.op) {
case QRhiProfiler::NewBuffer:
{
Buffer b;
b.lastTimestamp = e.timestamp;
b.resourceName = e.resourceName;
// type,0,usage,1,logical_size,84,effective_size,84,backing_gpu_buf_count,1,backing_cpu_buf_count,0
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("effective_size"))
b.effectiveSize = p.intValue;
else if (p.key == QByteArrayLiteral("backing_gpu_buf_count"))
b.backingGpuBufCount = p.intValue;
}
m_totalBufferApproxByteSize += b.effectiveSize * b.backingGpuBufCount;
m_peakBufferApproxByteSize = qMax(m_peakBufferApproxByteSize, m_totalBufferApproxByteSize);
m_buffers.insert(e.resource, b);
emit buffersTouched();
}
break;
case QRhiProfiler::ReleaseBuffer:
{
auto it = m_buffers.find(e.resource);
if (it != m_buffers.end()) {
m_totalBufferApproxByteSize -= it->effectiveSize * it->backingGpuBufCount;
m_buffers.erase(it);
emit buffersTouched();
}
}
break;
case QRhiProfiler::NewBufferStagingArea:
{
qint64 slot = -1;
qint64 size = 0;
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("slot"))
slot = p.intValue;
else if (p.key == QByteArrayLiteral("size"))
size = p.intValue;
}
if (slot >= 0 && slot < MAX_STAGING_SLOTS) {
auto it = m_buffers.find(e.resource);
if (it != m_buffers.end()) {
it->stagingExtraSize[slot] = size;
m_totalStagingBufferApproxByteSize += size;
m_peakStagingBufferApproxByteSize = qMax(m_peakStagingBufferApproxByteSize, m_totalStagingBufferApproxByteSize);
emit buffersTouched();
}
}
}
break;
case QRhiProfiler::ReleaseBufferStagingArea:
{
qint64 slot = -1;
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("slot"))
slot = p.intValue;
}
if (slot >= 0 && slot < MAX_STAGING_SLOTS) {
auto it = m_buffers.find(e.resource);
if (it != m_buffers.end()) {
m_totalStagingBufferApproxByteSize -= it->stagingExtraSize[slot];
it->stagingExtraSize[slot] = 0;
emit buffersTouched();
}
}
}
break;
case QRhiProfiler::NewTexture:
{
Texture t;
t.lastTimestamp = e.timestamp;
t.resourceName = e.resourceName;
// width,256,height,256,format,1,owns_native_resource,1,mip_count,9,layer_count,1,effective_sample_count,1,approx_byte_size,349524
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("approx_byte_size"))
t.approxByteSize = p.intValue;
else if (p.key == QByteArrayLiteral("owns_native_resource"))
t.ownsNativeResource = p.intValue;
}
if (t.ownsNativeResource) {
m_totalTextureApproxByteSize += t.approxByteSize;
m_peakTextureApproxByteSize = qMax(m_peakTextureApproxByteSize, m_totalTextureApproxByteSize);
}
m_textures.insert(e.resource, t);
emit texturesTouched();
}
break;
case QRhiProfiler::ReleaseTexture:
{
auto it = m_textures.find(e.resource);
if (it != m_textures.end()) {
if (it->ownsNativeResource)
m_totalTextureApproxByteSize -= it->approxByteSize;
m_textures.erase(it);
emit texturesTouched();
}
}
break;
case QRhiProfiler::NewTextureStagingArea:
{
qint64 slot = -1;
qint64 size = 0;
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("slot"))
slot = p.intValue;
else if (p.key == QByteArrayLiteral("size"))
size = p.intValue;
}
if (slot >= 0 && slot < MAX_STAGING_SLOTS) {
auto it = m_textures.find(e.resource);
if (it != m_textures.end()) {
it->stagingExtraSize[slot] = size;
m_totalTextureStagingBufferApproxByteSize += size;
m_peakTextureStagingBufferApproxByteSize = qMax(m_peakTextureStagingBufferApproxByteSize, m_totalTextureStagingBufferApproxByteSize);
emit texturesTouched();
}
}
}
break;
case QRhiProfiler::ReleaseTextureStagingArea:
{
qint64 slot = -1;
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("slot"))
slot = p.intValue;
}
if (slot >= 0 && slot < MAX_STAGING_SLOTS) {
auto it = m_textures.find(e.resource);
if (it != m_textures.end()) {
m_totalTextureStagingBufferApproxByteSize -= it->stagingExtraSize[slot];
it->stagingExtraSize[slot] = 0;
emit texturesTouched();
}
}
}
break;
case QRhiProfiler::ResizeSwapChain:
{
auto it = m_swapchains.find(e.resource);
if (it != m_swapchains.end())
m_totalSwapChainApproxByteSize -= it->approxByteSize;
SwapChain s;
s.lastTimestamp = e.timestamp;
s.resourceName = e.resourceName;
// width,1280,height,720,buffer_count,2,msaa_buffer_count,0,effective_sample_count,1,approx_total_byte_size,7372800
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("approx_total_byte_size"))
s.approxByteSize = p.intValue;
}
m_totalSwapChainApproxByteSize += s.approxByteSize;
m_peakSwapChainApproxByteSize = qMax(m_peakSwapChainApproxByteSize, m_totalSwapChainApproxByteSize);
m_swapchains.insert(e.resource, s);
emit swapchainsTouched();
}
break;
case QRhiProfiler::ReleaseSwapChain:
{
auto it = m_swapchains.find(e.resource);
if (it != m_swapchains.end()) {
m_totalSwapChainApproxByteSize -= it->approxByteSize;
m_swapchains.erase(it);
emit swapchainsTouched();
}
}
break;
case QRhiProfiler::GpuFrameTime:
{
// Fmin_ms_gpu_frame_time,0.15488,Fmax_ms_gpu_frame_time,0.494592,Favg_ms_gpu_frame_time,0.33462
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("min_ms_gpu_frame_time"))
m_lastGpuFrameTime.minTime = p.floatValue;
else if (p.key == QByteArrayLiteral("max_ms_gpu_frame_time"))
m_lastGpuFrameTime.maxTime = p.floatValue;
else if (p.key == QByteArrayLiteral("avg_ms_gpu_frame_time"))
m_lastGpuFrameTime.avgTime = p.floatValue;
}
emit gpuFrameTimeTouched();
}
break;
case QRhiProfiler::FrameToFrameTime:
{
// frames_since_resize,121,min_ms_frame_delta,9,max_ms_frame_delta,33,Favg_ms_frame_delta,16.1167
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("frames_since_resize"))
m_lastFrameTime.framesSinceResize = p.intValue;
else if (p.key == QByteArrayLiteral("min_ms_frame_delta"))
m_lastFrameTime.minDelta = p.intValue;
else if (p.key == QByteArrayLiteral("max_ms_frame_delta"))
m_lastFrameTime.maxDelta = p.intValue;
else if (p.key == QByteArrayLiteral("avg_ms_frame_delta"))
m_lastFrameTime.avgDelta = p.floatValue;
}
emit frameTimeTouched();
}
break;
case QRhiProfiler::GpuMemAllocStats:
{
// real_alloc_count,2,sub_alloc_count,154,total_size,10752,unused_size,50320896
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("real_alloc_count"))
m_lastGpuMemAllocStats.realAllocCount = p.intValue;
else if (p.key == QByteArrayLiteral("sub_alloc_count"))
m_lastGpuMemAllocStats.subAllocCount = p.intValue;
else if (p.key == QByteArrayLiteral("total_size"))
m_lastGpuMemAllocStats.totalSize = p.intValue;
else if (p.key == QByteArrayLiteral("unused_size"))
m_lastGpuMemAllocStats.unusedSize = p.intValue;
}
emit gpuMemAllocStatsTouched();
}
break;
default:
break;
}
}
class Server : public QTcpServer
{
Q_OBJECT
protected:
void incomingConnection(qintptr socketDescriptor) override;
signals:
void clientConnected();
void clientDisconnected();
void receiveStarted();
void lineReceived(const QByteArray &line);
private:
bool m_valid = false;
QTcpSocket m_socket;
QByteArray m_buf;
};
void Server::incomingConnection(qintptr socketDescriptor)
{
if (m_valid)
return;
m_socket.setSocketDescriptor(socketDescriptor);
m_valid = true;
emit clientConnected();
connect(&m_socket, &QAbstractSocket::readyRead, this, [this] {
bool receiveStartedSent = false;
m_buf += m_socket.readAll();
while (m_buf.contains('\n')) {
const int lfpos = m_buf.indexOf('\n');
const QByteArray line = m_buf.left(lfpos).trimmed();
m_buf = m_buf.mid(lfpos + 1);
if (!receiveStartedSent) {
receiveStartedSent = true;
emit receiveStarted();
}
emit lineReceived(line);
}
});
connect(&m_socket, &QAbstractSocket::disconnected, this, [this] {
if (m_valid) {
m_valid = false;
emit clientDisconnected();
}
});
}
int main(int argc, char **argv)
{
QApplication app(argc, argv);
Tracker tracker;
Parser parser;
QObject::connect(&parser, &Parser::eventReceived, &tracker, &Tracker::handleEvent);
Server server;
if (!server.listen(QHostAddress::Any, 30667))
qFatal("Failed to start server: %s", qPrintable(server.errorString()));
QVBoxLayout *layout = new QVBoxLayout;
QLabel *infoLabel = new QLabel(QLatin1String("<i>Launch a Qt Quick application with QSG_RHI_PROFILE=1 and QSG_RHI_PROFILE_HOST set to the IP address.<br>"
"(resource memory usage reporting works best with the Vulkan backend)</i>"));
layout->addWidget(infoLabel);
QGroupBox *groupBox = new QGroupBox(QLatin1String("RHI statistics"));
QVBoxLayout *groupLayout = new QVBoxLayout;
QLabel *buffersLabel = new QLabel;
QObject::connect(&tracker, &Tracker::buffersTouched, buffersLabel, [buffersLabel, &tracker] {
const QString msg = QString::asprintf("%d buffers with ca. %lld bytes of current memory (sub)allocations (peak %lld) + %lld bytes of known staging buffers (peak %lld)",
tracker.m_buffers.count(),
tracker.m_totalBufferApproxByteSize, tracker.m_peakBufferApproxByteSize,
tracker.m_totalStagingBufferApproxByteSize, tracker.m_peakStagingBufferApproxByteSize);
buffersLabel->setText(msg);
});
groupLayout->addWidget(buffersLabel);
QLabel *texturesLabel = new QLabel;
QObject::connect(&tracker, &Tracker::texturesTouched, texturesLabel, [texturesLabel, &tracker] {
const QString msg = QString::asprintf("%d textures with ca. %lld bytes of current memory (sub)allocations (peak %lld) + %lld bytes of known staging buffers (peak %lld)",
tracker.m_textures.count(),
tracker.m_totalTextureApproxByteSize, tracker.m_peakTextureApproxByteSize,
tracker.m_totalTextureStagingBufferApproxByteSize, tracker.m_peakTextureStagingBufferApproxByteSize);
texturesLabel->setText(msg);
});
groupLayout->addWidget(texturesLabel);
QLabel *swapchainsLabel = new QLabel;
QObject::connect(&tracker, &Tracker::swapchainsTouched, swapchainsLabel, [swapchainsLabel, &tracker] {
const QString msg = QString::asprintf("Estimated total swapchain color buffer size is %lld bytes (peak %lld)",
tracker.m_totalSwapChainApproxByteSize, tracker.m_peakSwapChainApproxByteSize);
swapchainsLabel->setText(msg);
});
groupLayout->addWidget(swapchainsLabel);
QLabel *frameTimeLabel = new QLabel;
QObject::connect(&tracker, &Tracker::frameTimeTouched, frameTimeLabel, [frameTimeLabel, &tracker] {
const QString msg = QString::asprintf("Frames since resize %lld Frame delta min %d ms max %d ms avg %f ms",
tracker.m_lastFrameTime.framesSinceResize,
tracker.m_lastFrameTime.minDelta,
tracker.m_lastFrameTime.maxDelta,
tracker.m_lastFrameTime.avgDelta);
frameTimeLabel->setText(msg);
});
groupLayout->addWidget(frameTimeLabel);
QLabel *gpuFrameTimeLabel = new QLabel;
QObject::connect(&tracker, &Tracker::gpuFrameTimeTouched, gpuFrameTimeLabel, [gpuFrameTimeLabel, &tracker] {
const QString msg = QString::asprintf("GPU frame time min %f ms max %f ms avg %f ms",
tracker.m_lastGpuFrameTime.minTime,
tracker.m_lastGpuFrameTime.maxTime,
tracker.m_lastGpuFrameTime.avgTime);
gpuFrameTimeLabel->setText(msg);
});
groupLayout->addWidget(gpuFrameTimeLabel);
QLabel *gpuMemAllocStatsLabel = new QLabel;
QObject::connect(&tracker, &Tracker::gpuMemAllocStatsTouched, gpuMemAllocStatsLabel, [gpuMemAllocStatsLabel, &tracker] {
const QString msg = QString::asprintf("GPU memory allocator status: %lld real allocations %lld sub-allocations %lld total bytes %lld unused bytes",
tracker.m_lastGpuMemAllocStats.realAllocCount,
tracker.m_lastGpuMemAllocStats.subAllocCount,
tracker.m_lastGpuMemAllocStats.totalSize,
tracker.m_lastGpuMemAllocStats.unusedSize);
gpuMemAllocStatsLabel->setText(msg);
});
groupLayout->addWidget(gpuMemAllocStatsLabel);
groupBox->setLayout(groupLayout);
layout->addWidget(groupBox);
QTextEdit *rawLog = new QTextEdit;
rawLog->setReadOnly(true);
layout->addWidget(rawLog);
QObject::connect(&server, &Server::clientConnected, rawLog, [rawLog] {
rawLog->append(QLatin1String("\nCONNECTED\n"));
});
QObject::connect(&server, &Server::clientDisconnected, rawLog, [rawLog, &tracker] {
rawLog->append(QLatin1String("\nDISCONNECTED\n"));
tracker.reset();
});
QObject::connect(&server, &Server::receiveStarted, rawLog, [rawLog] {
rawLog->setFontItalic(true);
rawLog->append(QLatin1String("[") + QTime::currentTime().toString() + QLatin1String("]"));
rawLog->setFontItalic(false);
});
QObject::connect(&server, &Server::lineReceived, rawLog, [rawLog, &parser](const QByteArray &line) {
rawLog->append(QString::fromUtf8(line));
parser.feed(line);
});
QWidget w;
w.resize(800, 600);
w.setLayout(layout);
w.show();
return app.exec();
}
#include "qrhiprof.moc"

View File

@ -0,0 +1,6 @@
TEMPLATE = app
QT += network widgets gui-private
SOURCES = \
qrhiprof.cpp

22
tests/manual/rhi/rhi.pro Normal file
View File

@ -0,0 +1,22 @@
TEMPLATE = subdirs
SUBDIRS += \
hellominimalcrossgfxtriangle \
compressedtexture_bc1 \
compressedtexture_bc1_subupload \
texuploads \
msaatexture \
msaarenderbuffer \
cubemap \
multiwindow \
multiwindow_threaded \
triquadcube \
offscreen \
floattexture \
mrt \
shadowmap
qtConfig(widgets) {
SUBDIRS += \
qrhiprof
}

View File

@ -0,0 +1,4 @@
qsb --glsl "120,300 es" --hlsl 50 --msl 12 -c shadowmap.vert -o shadowmap.vert.qsb
qsb --glsl "120,300 es" --hlsl 50 --msl 12 -c shadowmap.frag -o shadowmap.frag.qsb
qsb --glsl "120,300 es" --hlsl 50 --msl 12 -c main.vert -o main.vert.qsb
qsb --glsl "120,300 es" --hlsl 50 --msl 12 -c main.frag -o main.frag.qsb

View File

@ -0,0 +1,4 @@
qsb --glsl "120,300 es" --hlsl 50 --msl 12 shadowmap.vert -o shadowmap.vert.qsb
qsb --glsl "120,300 es" --hlsl 50 --msl 12 shadowmap.frag -o shadowmap.frag.qsb
qsb --glsl "120,300 es" --hlsl 50 --msl 12 main.vert -o main.vert.qsb
qsb --glsl "120,300 es" --hlsl 50 --msl 12 main.frag -o main.frag.qsb

View File

@ -0,0 +1,30 @@
#version 440
layout(location = 0) in vec4 vLCVertPos;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2DShadow shadowMap;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
mat4 lightMvp;
mat4 shadowBias;
int useShadow;
} ubuf;
void main()
{
vec4 adjustedLcVertPos = vLCVertPos;
adjustedLcVertPos.z -= 0.0001; // bias to avoid acne
// no textureProj, that seems to end up not doing the perspective divide for z (?)
vec3 v = adjustedLcVertPos.xyz / adjustedLcVertPos.w;
float sc = texture(shadowMap, v); // sampler is comparison enabled so compares to z
float shadowFactor = 0.2;
if (sc > 0 || ubuf.useShadow == 0)
shadowFactor = 1.0;
fragColor = vec4(0.5, 0.3 + ubuf.useShadow * 0.2, 0.7, 1.0) * shadowFactor;
}

Binary file not shown.

View File

@ -0,0 +1,20 @@
#version 440
layout(location = 0) in vec4 position;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
mat4 lightMvp;
mat4 shadowBias;
int useShadow;
} ubuf;
out gl_PerVertex { vec4 gl_Position; };
layout(location = 0) out vec4 vLCVertPos;
void main()
{
vLCVertPos = ubuf.shadowBias * ubuf.lightMvp * position;
gl_Position = ubuf.mvp * position;
}

Binary file not shown.

View File

@ -0,0 +1,304 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "../shared/examplefw.h"
#include "../shared/cube.h"
// Depth texture / shadow sampler / shadow map example.
// Not available on GLES 2.0.
static float quadVertexData[] =
{ // Y up, CCW, x-y-z
-0.5f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
};
static quint16 quadIndexData[] =
{
0, 1, 2, 0, 2, 3
};
struct {
QVector<QRhiResource *> releasePool;
QRhiBuffer *vbuf = nullptr;
QRhiBuffer *ibuf = nullptr;
QRhiBuffer *ubuf = nullptr;
QRhiSampler *shadowSampler = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
QMatrix4x4 winProj;
float cubeRot = 0;
QRhiTextureRenderTarget *rt = nullptr;
QRhiRenderPassDescriptor *rtRp = nullptr;
QRhiTexture *shadowMap = nullptr;
QRhiShaderResourceBindings *shadowSrb = nullptr;
QRhiGraphicsPipeline *shadowPs = nullptr;
} d;
const int UBLOCK_SIZE = 64 * 3 + 4;
const int SHADOW_UBLOCK_SIZE = 64 * 1;
const int UBUF_SLOTS = 4; // 2 objects * 2 passes with different cameras
void Window::customInit()
{
if (!m_r->isTextureFormatSupported(QRhiTexture::D32F))
qFatal("Depth texture is not supported");
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(quadVertexData) + sizeof(cube));
d.vbuf->build();
d.releasePool << d.vbuf;
d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(quadIndexData));
d.ibuf->build();
d.releasePool << d.ibuf;
const int oneRoundedUniformBlockSize = m_r->ubufAligned(UBLOCK_SIZE);
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, UBUF_SLOTS * oneRoundedUniformBlockSize);
d.ubuf->build();
d.releasePool << d.ubuf;
d.shadowMap = m_r->newTexture(QRhiTexture::D32F, QSize(1024, 1024), 1, QRhiTexture::RenderTarget);
d.releasePool << d.shadowMap;
d.shadowMap->build();
d.shadowSampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
d.releasePool << d.shadowSampler;
d.shadowSampler->setTextureCompareOp(QRhiSampler::Less);
d.shadowSampler->build();
d.srb = m_r->newShaderResourceBindings();
d.releasePool << d.srb;
const QRhiShaderResourceBinding::StageFlags stages = QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage;
d.srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, stages, d.ubuf, UBLOCK_SIZE),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.shadowMap, d.shadowSampler) });
d.srb->build();
d.ps = m_r->newGraphicsPipeline();
d.releasePool << d.ps;
d.ps->setShaderStages({
{ QRhiGraphicsShaderStage::Vertex, getShader(QLatin1String(":/main.vert.qsb")) },
{ QRhiGraphicsShaderStage::Fragment, getShader(QLatin1String(":/main.frag.qsb")) }
});
d.ps->setDepthTest(true);
d.ps->setDepthWrite(true);
// fits both the quad and cube vertex data
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 3 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float3, 0 },
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->build();
d.initialUpdates = m_r->nextResourceUpdateBatch();
d.initialUpdates->uploadStaticBuffer(d.vbuf, 0, sizeof(quadVertexData), quadVertexData);
d.initialUpdates->uploadStaticBuffer(d.vbuf, sizeof(quadVertexData), sizeof(cube), cube);
d.initialUpdates->uploadStaticBuffer(d.ibuf, quadIndexData);
QRhiTextureRenderTargetDescription rtDesc;
rtDesc.setDepthTexture(d.shadowMap);
d.rt = m_r->newTextureRenderTarget(rtDesc);
d.releasePool << d.rt;
d.rtRp = d.rt->newCompatibleRenderPassDescriptor();
d.releasePool << d.rtRp;
d.rt->setRenderPassDescriptor(d.rtRp);
d.rt->build();
d.shadowSrb = m_r->newShaderResourceBindings();
d.releasePool << d.shadowSrb;
d.shadowSrb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, stages, d.ubuf, SHADOW_UBLOCK_SIZE) });
d.shadowSrb->build();
d.shadowPs = m_r->newGraphicsPipeline();
d.releasePool << d.shadowPs;
d.shadowPs->setShaderStages({
{ QRhiGraphicsShaderStage::Vertex, getShader(QLatin1String(":/shadowmap.vert.qsb")) },
{ QRhiGraphicsShaderStage::Fragment, getShader(QLatin1String(":/shadowmap.frag.qsb")) }
});
d.shadowPs->setDepthTest(true);
d.shadowPs->setDepthWrite(true);
inputLayout.setBindings({
{ 3 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float3, 0 }
});
d.shadowPs->setVertexInputLayout(inputLayout);
d.shadowPs->setShaderResourceBindings(d.shadowSrb);
d.shadowPs->setRenderPassDescriptor(d.rtRp);
d.shadowPs->build();
}
void Window::customRelease()
{
qDeleteAll(d.releasePool);
d.releasePool.clear();
}
static void enqueueScene(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb, int oneRoundedUniformBlockSize, int firstUbufSlot)
{
QRhiCommandBuffer::DynamicOffset ubufOffset(0, quint32(firstUbufSlot * oneRoundedUniformBlockSize));
// draw the ground (the quad)
cb->setShaderResources(srb, 1, &ubufOffset);
QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0);
cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16);
cb->drawIndexed(6);
// Draw the object (the cube). Both vertex and uniform data are in the same
// buffer, right after the quad's.
ubufOffset.second += oneRoundedUniformBlockSize;
cb->setShaderResources(srb, 1, &ubufOffset);
vbufBinding.second = sizeof(quadVertexData);
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(36);
}
void Window::customRender()
{
const QSize outputSizeInPixels = m_sc->currentPixelSize();
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
}
const int oneRoundedUniformBlockSize = m_r->ubufAligned(UBLOCK_SIZE);
QMatrix4x4 shadowBias;
// fill it in column-major order to keep our sanity (ctor would take row-major)
float *sbp = shadowBias.data();
if (m_r->isClipDepthZeroToOne()) {
// convert x, y [-1, 1] -> [0, 1]
*sbp++ = 0.5f; *sbp++ = 0.0f; *sbp++ = 0.0f; *sbp++ = 0.0f;
*sbp++ = 0.0f; *sbp++ = m_r->isYUpInNDC() ? -0.5f : 0.5f; *sbp++ = 0.0f; *sbp++ = 0.0f;
*sbp++ = 0.0f; *sbp++ = 0.0f; *sbp++ = 1.0f; *sbp++ = 0.0f;
*sbp++ = 0.5f; *sbp++ = 0.5f; *sbp++ = 0.0f; *sbp++ = 1.0f;
} else {
// convert x, y, z [-1, 1] -> [0, 1]
*sbp++ = 0.5f; *sbp++ = 0.0f; *sbp++ = 0.0f; *sbp++ = 0.0f;
*sbp++ = 0.0f; *sbp++ = 0.5f; *sbp++ = 0.0f; *sbp++ = 0.0f;
*sbp++ = 0.0f; *sbp++ = 0.0f; *sbp++ = 0.5f; *sbp++ = 0.0f;
*sbp++ = 0.5f; *sbp++ = 0.5f; *sbp++ = 0.5f; *sbp++ = 1.0f;
}
const QVector3D lightPos(5, 10, 10);
QMatrix4x4 lightViewProj = m_r->clipSpaceCorrMatrix();
lightViewProj.perspective(45.0f, 1, 0.1f, 100.0f);
lightViewProj.lookAt(lightPos, QVector3D(0, 0, 0), QVector3D(0, 1, 0));
// uniform data for the ground
if (d.winProj != m_proj) {
d.winProj = m_proj;
QMatrix4x4 m;
m.scale(4.0f);
m.rotate(-60, 1, 0, 0);
// for the main pass
const QMatrix4x4 mvp = m_proj * m; // m_proj is in fact projection * view
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
const QMatrix4x4 shadowMvp = lightViewProj * m;
u->updateDynamicBuffer(d.ubuf, 64, 64, shadowMvp.constData());
u->updateDynamicBuffer(d.ubuf, 128, 64, shadowBias.constData());
qint32 useShadows = 1;
u->updateDynamicBuffer(d.ubuf, 192, 4, &useShadows);
// for the shadow pass
u->updateDynamicBuffer(d.ubuf, 2 * oneRoundedUniformBlockSize, 64, shadowMvp.constData());
}
// uniform data for the rotating cube
QMatrix4x4 m;
m.translate(0, 0.5f, 2);
m.scale(0.2f);
m.rotate(d.cubeRot, 0, 1, 0);
m.rotate(45, 1, 0, 0);
d.cubeRot += 1;
// for the main pass
const QMatrix4x4 mvp = m_proj * m;
u->updateDynamicBuffer(d.ubuf, oneRoundedUniformBlockSize, 64, mvp.constData());
const QMatrix4x4 shadowMvp = lightViewProj * m;
u->updateDynamicBuffer(d.ubuf, oneRoundedUniformBlockSize + 64, 64, shadowMvp.constData());
u->updateDynamicBuffer(d.ubuf, oneRoundedUniformBlockSize + 128, 64, shadowBias.constData());
qint32 useShadows = 0;
u->updateDynamicBuffer(d.ubuf, oneRoundedUniformBlockSize + 192, 4, &useShadows);
// for the shadow pass
u->updateDynamicBuffer(d.ubuf, 3 * oneRoundedUniformBlockSize, 64, shadowMvp.constData());
cb->resourceUpdate(u);
// shadow pass
const QSize shadowMapSize = d.shadowMap->pixelSize();
cb->beginPass(d.rt, QColor(), { 1.0f, 0 });
cb->setGraphicsPipeline(d.shadowPs);
cb->setViewport({ 0, 0, float(shadowMapSize.width()), float(shadowMapSize.height()) });
enqueueScene(cb, d.shadowSrb, oneRoundedUniformBlockSize, 2);
cb->endPass();
// main pass
cb->beginPass(m_sc->currentFrameRenderTarget(), QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f), { 1.0f, 0 });
cb->setGraphicsPipeline(d.ps);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
enqueueScene(cb, d.srb, oneRoundedUniformBlockSize, 0);
cb->endPass();
}

Some files were not shown because too many files have changed in this diff Show More