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>
parent
c143161608
commit
53599592e0
|
|
@ -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
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
|
|
@ -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
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
|
|
@ -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
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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, ¶ms);
|
||||
\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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
@ -13,6 +13,7 @@ SUBDIRS = \
|
|||
text \
|
||||
util \
|
||||
itemmodels \
|
||||
rhi
|
||||
|
||||
!qtConfig(opengl)|winrt: SUBDIRS -= qopengl qopenglconfig
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
TARGET = tst_qrhi
|
||||
CONFIG += testcase
|
||||
|
||||
QT += testlib gui-private
|
||||
|
||||
SOURCES += tst_qrhi.cpp
|
||||
|
||||
RESOURCES += qrhi.qrc
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>data</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
@ -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)
|
||||
|
|
@ -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.
|
|
@ -0,0 +1,8 @@
|
|||
TARGET = tst_qshader
|
||||
CONFIG += testcase
|
||||
|
||||
QT += testlib gui-private
|
||||
|
||||
SOURCES += tst_qshader.cpp
|
||||
|
||||
RESOURCES += qshader.qrc
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>data</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
@ -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)
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
TEMPLATE=subdirs
|
||||
SUBDIRS= \
|
||||
qshader \
|
||||
qrhi
|
||||
|
|
@ -53,7 +53,8 @@ shortcuts \
|
|||
dialogs \
|
||||
windowtransparency \
|
||||
unc \
|
||||
qtabbar
|
||||
qtabbar \
|
||||
rhi
|
||||
|
||||
!qtConfig(openssl): SUBDIRS -= qssloptions
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
compressedtexture_bc1.cpp
|
||||
|
||||
RESOURCES = compressedtexture_bc1.qrc
|
||||
|
|
@ -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>
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
compressedtexture_bc1_subupload.cpp
|
||||
|
||||
RESOURCES = compressedtexture_bc1_subupload.qrc
|
||||
|
|
@ -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>
|
||||
|
|
@ -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 |
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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.
|
|
@ -0,0 +1,8 @@
|
|||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
cubemap.cpp
|
||||
|
||||
RESOURCES = cubemap.qrc
|
||||
|
|
@ -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>
|
||||
|
|
@ -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.
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
floattexture.cpp
|
||||
|
||||
RESOURCES = floattexture.qrc
|
||||
|
|
@ -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>
|
||||
|
|
@ -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, ¶ms);
|
||||
}
|
||||
|
||||
#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, ¶ms);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
if (graphicsApi == Vulkan) {
|
||||
QRhiVulkanInitParams params;
|
||||
params.inst = vulkanInstance();
|
||||
params.window = this;
|
||||
m_r = QRhi::create(QRhi::Vulkan, ¶ms);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (graphicsApi == D3D11) {
|
||||
QRhiD3D11InitParams params;
|
||||
m_r = QRhi::create(QRhi::D3D11, ¶ms);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_DARWIN
|
||||
if (graphicsApi == Metal) {
|
||||
QRhiMetalInitParams params;
|
||||
m_r = QRhi::create(QRhi::Metal, ¶ms);
|
||||
}
|
||||
#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();
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
hellominimalcrossgfxtriangle.cpp
|
||||
|
||||
RESOURCES = hellominimalcrossgfxtriangle.qrc
|
||||
|
|
@ -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>
|
||||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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.
|
|
@ -0,0 +1,8 @@
|
|||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
mrt.cpp
|
||||
|
||||
RESOURCES = mrt.qrc
|
||||
|
|
@ -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>
|
||||
|
|
@ -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.
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
msaarenderbuffer.cpp
|
||||
|
||||
RESOURCES = msaarenderbuffer.qrc
|
||||
|
|
@ -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>
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
msaatexture.cpp
|
||||
|
||||
RESOURCES = msaatexture.qrc
|
||||
|
|
@ -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>
|
||||
|
|
@ -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, ¶ms);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
if (graphicsApi == Vulkan) {
|
||||
QRhiVulkanInitParams params;
|
||||
params.inst = r.instance;
|
||||
//params.window = this;
|
||||
r.r = QRhi::create(QRhi::Vulkan, ¶ms);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (graphicsApi == D3D11) {
|
||||
QRhiD3D11InitParams params;
|
||||
params.enableDebugLayer = true;
|
||||
r.r = QRhi::create(QRhi::D3D11, ¶ms);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_DARWIN
|
||||
if (graphicsApi == Metal) {
|
||||
QRhiMetalInitParams params;
|
||||
r.r = QRhi::create(QRhi::Metal, ¶ms);
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
TEMPLATE = app
|
||||
|
||||
QT += gui-private widgets
|
||||
|
||||
SOURCES = \
|
||||
multiwindow.cpp
|
||||
|
||||
RESOURCES = multiwindow.qrc
|
||||
|
|
@ -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>
|
||||
|
|
@ -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, ¶ms, rhiFlags);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
if (graphicsApi == Vulkan) {
|
||||
QRhiVulkanInitParams params;
|
||||
params.inst = instance;
|
||||
params.window = window;
|
||||
r = QRhi::create(QRhi::Vulkan, ¶ms, rhiFlags);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (graphicsApi == D3D11) {
|
||||
QRhiD3D11InitParams params;
|
||||
params.enableDebugLayer = true;
|
||||
r = QRhi::create(QRhi::D3D11, ¶ms, rhiFlags);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_DARWIN
|
||||
if (graphicsApi == Metal) {
|
||||
QRhiMetalInitParams params;
|
||||
r = QRhi::create(QRhi::Metal, ¶ms, 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
TEMPLATE = app
|
||||
|
||||
QT += gui-private widgets
|
||||
|
||||
SOURCES = \
|
||||
multiwindow_threaded.cpp \
|
||||
window.cpp
|
||||
|
||||
HEADERS = \
|
||||
window.h
|
||||
|
||||
RESOURCES = multiwindow_threaded.qrc
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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, ¶ms);
|
||||
}
|
||||
|
||||
#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, ¶ms);
|
||||
} 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, ¶ms);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (graphicsApi == D3D11) {
|
||||
QRhiD3D11InitParams params;
|
||||
params.enableDebugLayer = true;
|
||||
r = QRhi::create(QRhi::D3D11, ¶ms);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_DARWIN
|
||||
if (graphicsApi == Metal) {
|
||||
QRhiMetalInitParams params;
|
||||
r = QRhi::create(QRhi::Metal, ¶ms);
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
TEMPLATE = app
|
||||
CONFIG += console
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
offscreen.cpp
|
||||
|
||||
RESOURCES = offscreen.qrc
|
||||
|
|
@ -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>
|
||||
|
|
@ -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"
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
TEMPLATE = app
|
||||
|
||||
QT += network widgets gui-private
|
||||
|
||||
SOURCES = \
|
||||
qrhiprof.cpp
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
|
@ -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.
|
|
@ -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
Loading…
Reference in New Issue