aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@theqtcompany.com>2016-03-03 16:44:20 +0100
committerAndy Nichols <andy.nichols@theqtcompany.com>2016-03-07 16:13:06 +0000
commitc64d60e6946c57733fa03acf3d544f9adb5e05a9 (patch)
tree4d3a3ef3a0f0fada6643fec7bc0ce3c981d0005a
parent9f764891c142ddf318fc3fef298686c922fd2cc6 (diff)
D3D12: Basic image support
For now there is no mipmapping, no atlases, and all image data is converted to RGBA8888. Regardless, this already makes it possible to use basic Image elements. Follow the same pattern as with rectangles: most of the code is shared via a material-less, abstract QSGDefaultNoMaterialImageNode, with the material-related bits provided by QSGDefaultImageNode for GL and QSGD3D12ImageNode for D3D. Also fixes the harmless but incorrect miscalculation of the VBV size for indexed draws. Image data is copied into an intermediate upload buffer when invoking the render context's createTexture(). The upload is triggered on a separate copy command queue. The main command queue is synchronized via a fence before executing the commands for the current frame. Root signatures are now cached more cleverly and include zero or more SRVs as parameter 1. This involves the engine having to manage a shader (GPU) visible CBV-SRV-UAV descriptor heap as well. Change-Id: I67c35577ff2677e1c7776dd8e5011860e2d78b00 Reviewed-by: Andy Nichols <andy.nichols@theqtcompany.com>
-rw-r--r--.gitignore4
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/d3d12.pri8
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp4
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp548
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h90
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h98
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12imagenode.cpp120
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12imagenode_p.h82
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12material.cpp79
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12material_p.h38
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext.cpp14
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext_p.h1
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp19
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12texture.cpp124
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12texture_p.h88
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri13
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/shaders/texture.hlsl33
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.h4
-rw-r--r--src/quick/scenegraph/qsgdefaultimagenode.cpp214
-rw-r--r--src/quick/scenegraph/qsgdefaultimagenode_p.h66
-rw-r--r--tests/manual/nodetypes/Images.qml60
-rw-r--r--tests/manual/nodetypes/main.qml3
-rw-r--r--tests/manual/nodetypes/nodetypes.cpp1
-rw-r--r--tests/manual/nodetypes/nodetypes.pro2
-rw-r--r--tests/manual/nodetypes/nodetypes.qrc3
-rw-r--r--tests/manual/nodetypes/qt.pngbin0 -> 11917 bytes
26 files changed, 1436 insertions, 280 deletions
diff --git a/.gitignore b/.gitignore
index 67c40bff90..f33da3c8b3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -283,6 +283,4 @@ src/qml/udis86_itab.c
src/qml/udis86_itab.h
# Generated HLSL bytecode headers
-hlsl_vs_*.h
-hlsl_ps_*.h
-hlsl_cs_*.h
+*.hlslh
diff --git a/src/quick/scenegraph/adaptations/d3d12/d3d12.pri b/src/quick/scenegraph/adaptations/d3d12/d3d12.pri
index 8123b9d19b..e61407460f 100644
--- a/src/quick/scenegraph/adaptations/d3d12/d3d12.pri
+++ b/src/quick/scenegraph/adaptations/d3d12/d3d12.pri
@@ -5,7 +5,9 @@ SOURCES += \
$$PWD/qsgd3d12context.cpp \
$$PWD/qsgd3d12rendercontext.cpp \
$$PWD/qsgd3d12rectanglenode.cpp \
- $$PWD/qsgd3d12material.cpp
+ $$PWD/qsgd3d12material.cpp \
+ $$PWD/qsgd3d12texture.cpp \
+ $$PWD/qsgd3d12imagenode.cpp
NO_PCH_SOURCES += \
$$PWD/qsgd3d12engine.cpp
@@ -19,7 +21,9 @@ HEADERS += \
$$PWD/qsgd3d12engine_p.h \
$$PWD/qsgd3d12engine_p_p.h \
$$PWD/qsgd3d12rectanglenode_p.h \
- $$PWD/qsgd3d12material_p.h
+ $$PWD/qsgd3d12material_p.h \
+ $$PWD/qsgd3d12texture_p.h \
+ $$PWD/qsgd3d12imagenode_p.h
LIBS += -ldxgi -ld3d12
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp
index b75d6ebb89..727109cc9c 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp
@@ -40,6 +40,7 @@
#include "qsgd3d12context_p.h"
#include "qsgd3d12rendercontext_p.h"
#include "qsgd3d12rectanglenode_p.h"
+#include "qsgd3d12imagenode_p.h"
QT_BEGIN_NAMESPACE
@@ -55,8 +56,7 @@ QSGRectangleNode *QSGD3D12Context::createRectangleNode()
QSGImageNode *QSGD3D12Context::createImageNode()
{
- Q_UNREACHABLE();
- return nullptr;
+ return new QSGD3D12ImageNode;
}
QSGPainterNode *QSGD3D12Context::createPainterNode(QQuickPaintedItem *item)
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp
index 6b7e01726e..a063c78afa 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp
@@ -41,6 +41,7 @@
#include "qsgd3d12engine_p_p.h"
#include <QString>
#include <QColor>
+#include <QtCore/private/qsimd_p.h>
QT_BEGIN_NAMESPACE
@@ -52,22 +53,31 @@ QT_BEGIN_NAMESPACE
DECLARE_DEBUG_VAR(render)
-// Recommended reading before moving further: https://github.com/Microsoft/DirectXTK/wiki/ComPtr
-// Note esp. operator= vs. Attach and operator& vs. GetAddressOf
+static const int MAX_CACHED_ROOTSIG = 16;
+static const int MAX_CACHED_PSO = 64;
-static const int MAX_DESCRIPTORS_PER_HEAP = 256;
+static const int MAX_GPU_CBVSRVUAV_DESCRIPTORS = 256;
-QSGD3D12DescriptorHandle QSGD3D12DescriptorHeapManager::allocate(D3D12_DESCRIPTOR_HEAP_TYPE type, Flags flags)
+static const int BUCKETS_PER_HEAP = 8; // must match freeMap
+static const int DESCRIPTORS_PER_BUCKET = 32; // the bit map (freeMap) is quint32
+static const int MAX_DESCRIPTORS_PER_HEAP = BUCKETS_PER_HEAP * DESCRIPTORS_PER_BUCKET;
+
+D3D12_CPU_DESCRIPTOR_HANDLE QSGD3D12CPUDescriptorHeapManager::allocate(D3D12_DESCRIPTOR_HEAP_TYPE type)
{
- QSGD3D12DescriptorHandle h;
+ D3D12_CPU_DESCRIPTOR_HANDLE h = {};
for (Heap &heap : m_heaps) {
- if (heap.type == type && heap.count < MAX_DESCRIPTORS_PER_HEAP) {
- h = heap.start;
- h.cpu.ptr += heap.count * heap.handleSize;
- if (h.gpu.ptr)
- h.gpu.ptr += heap.count * heap.handleSize;
- ++heap.count;
- return h;
+ if (heap.type == type) {
+ for (int bucket = 0; bucket < _countof(heap.freeMap); ++bucket)
+ if (heap.freeMap[bucket]) {
+ unsigned long freePos = _bit_scan_forward(heap.freeMap[bucket]);
+ heap.freeMap[bucket] &= ~(1UL << freePos);
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("descriptor handle type %x reserve in bucket %d index %d", type, bucket, freePos);
+ freePos += bucket * DESCRIPTORS_PER_BUCKET;
+ h = heap.start;
+ h.ptr += freePos * heap.handleSize;
+ return h;
+ }
}
}
@@ -78,8 +88,7 @@ QSGD3D12DescriptorHandle QSGD3D12DescriptorHeapManager::allocate(D3D12_DESCRIPTO
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.NumDescriptors = MAX_DESCRIPTORS_PER_HEAP;
heapDesc.Type = type;
- if (flags & ShaderVisible)
- heapDesc.Flags |= D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
+ // The heaps created here are _never_ shader-visible.
HRESULT hr = m_device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&heap.heap));
if (FAILED(hr)) {
@@ -87,20 +96,41 @@ QSGD3D12DescriptorHandle QSGD3D12DescriptorHeapManager::allocate(D3D12_DESCRIPTO
return h;
}
- heap.start.cpu = heap.heap->GetCPUDescriptorHandleForHeapStart();
+ heap.start = heap.heap->GetCPUDescriptorHandleForHeapStart();
+
if (Q_UNLIKELY(debug_render()))
- qDebug("type %x start is %llu", type, heap.start.cpu.ptr);
- if (flags & ShaderVisible)
- heap.start.gpu = heap.heap->GetGPUDescriptorHandleForHeapStart();
+ qDebug("new descriptor heap, type %x, start %llu", type, heap.start.ptr);
+
+ heap.freeMap[0] = 0xFFFFFFFE;
+ for (int i = 1; i < _countof(heap.freeMap); ++i)
+ heap.freeMap[i] = 0xFFFFFFFF;
- heap.count = 1;
h = heap.start;
+
m_heaps.append(heap);
return h;
}
-void QSGD3D12DescriptorHeapManager::initialize(ID3D12Device *device)
+void QSGD3D12CPUDescriptorHeapManager::release(D3D12_CPU_DESCRIPTOR_HANDLE handle, D3D12_DESCRIPTOR_HEAP_TYPE type)
+{
+ for (Heap &heap : m_heaps) {
+ if (heap.type == type
+ && handle.ptr >= heap.start.ptr
+ && handle.ptr < heap.start.ptr + heap.handleSize * MAX_DESCRIPTORS_PER_HEAP) {
+ unsigned long pos = (handle.ptr - heap.start.ptr) / heap.handleSize;
+ const int bucket = pos / DESCRIPTORS_PER_BUCKET;
+ const int indexInBucket = pos - bucket * DESCRIPTORS_PER_BUCKET;
+ heap.freeMap[bucket] |= 1UL << indexInBucket;
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("free descriptor handle type %x bucket %d index %d", type, bucket, indexInBucket);
+ return;
+ }
+ }
+ qWarning("QSGD3D12CPUDescriptorHeapManager: Attempted to release untracked descriptor handle %llu of type %d", handle.ptr, type);
+}
+
+void QSGD3D12CPUDescriptorHeapManager::initialize(ID3D12Device *device)
{
m_device = device;
@@ -108,7 +138,7 @@ void QSGD3D12DescriptorHeapManager::initialize(ID3D12Device *device)
m_handleSizes[i] = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE(i));
}
-void QSGD3D12DescriptorHeapManager::releaseResources()
+void QSGD3D12CPUDescriptorHeapManager::releaseResources()
{
for (Heap &heap : m_heaps)
heap.heap = nullptr;
@@ -331,11 +361,11 @@ void QSGD3D12Engine::queueSetStencilRef(quint32 ref)
d->queueSetStencilRef(ref);
}
-void QSGD3D12Engine::queueDraw(QSGGeometry::DrawingMode mode, int count, int vboOffset, int vboStride,
+void QSGD3D12Engine::queueDraw(QSGGeometry::DrawingMode mode, int count, int vboOffset, int vboSize, int vboStride,
int cboOffset,
int startIndexIndex, QSGD3D12Format indexFormat)
{
- d->queueDraw(mode, count, vboOffset, vboStride, cboOffset, startIndexIndex, indexFormat);
+ d->queueDraw(mode, count, vboOffset, vboSize, vboStride, cboOffset, startIndexIndex, indexFormat);
}
void QSGD3D12Engine::present()
@@ -348,6 +378,31 @@ void QSGD3D12Engine::waitGPU()
d->waitGPU();
}
+uint QSGD3D12Engine::createTexture(QImage::Format format, const QSize &size, TextureCreateFlags flags)
+{
+ return d->createTexture(format, size, flags);
+}
+
+void QSGD3D12Engine::releaseTexture(uint id)
+{
+ d->releaseTexture(id);
+}
+
+SIZE_T QSGD3D12Engine::textureSRV(uint id) const
+{
+ return d->textureSRV(id);
+}
+
+void QSGD3D12Engine::queueTextureUpload(uint id, const QImage &image, TextureUploadFlags flags)
+{
+ return d->queueTextureUpload(id, image, flags);
+}
+
+void QSGD3D12Engine::addFrameTextureDep(uint id)
+{
+ d->addFrameTextureDep(id);
+}
+
quint32 QSGD3D12Engine::alignedConstantBufferSize(quint32 size)
{
return (size + D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1) & ~(D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1);
@@ -411,8 +466,11 @@ void QSGD3D12EnginePrivate::releaseResources()
if (!initialized)
return;
- bundleAllocator = nullptr;
commandAllocator = nullptr;
+ copyCommandAllocator = nullptr;
+
+ commandList = nullptr;
+ copyCommandList = nullptr;
depthStencil = nullptr;
for (int i = 0; i < swapChainBufferCount; ++i)
@@ -422,17 +480,19 @@ void QSGD3D12EnginePrivate::releaseResources()
indexBuffer = nullptr;
constantBuffer = nullptr;
- for (ComPtr<ID3D12PipelineState> &ps : m_psoCache)
- ps = nullptr;
- m_psoCache.clear();
- m_rootSig = nullptr;
+ psoCache.clear();
+ rootSigCache.clear();
+ textures.clear();
- descHeapManager.releaseResources();
+ gpuCbvSrvUavHeap = nullptr;
+ cpuDescHeapManager.releaseResources();
commandQueue = nullptr;
+ copyCommandQueue = nullptr;
swapChain = nullptr;
delete presentFence;
+ textureUploadFence = nullptr;
deviceManager()->unref();
@@ -463,12 +523,17 @@ void QSGD3D12EnginePrivate::initialize(QWindow *w)
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
-
if (FAILED(device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue)))) {
qWarning("Failed to create command queue");
return;
}
+ queueDesc.Type = D3D12_COMMAND_LIST_TYPE_COPY;
+ if (FAILED(device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&copyCommandQueue)))) {
+ qWarning("Failed to create copy command queue");
+ return;
+ }
+
DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
swapChainDesc.BufferCount = swapChainBufferCount;
swapChainDesc.BufferDesc.Width = window->width() * window->devicePixelRatio();
@@ -498,12 +563,22 @@ void QSGD3D12EnginePrivate::initialize(QWindow *w)
return;
}
- if (FAILED(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_BUNDLE, IID_PPV_ARGS(&bundleAllocator)))) {
- qWarning("Failed to create command bundle allocator");
+ if (FAILED(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_COPY, IID_PPV_ARGS(&copyCommandAllocator)))) {
+ qWarning("Failed to create copy command allocator");
return;
}
- descHeapManager.initialize(device);
+ D3D12_DESCRIPTOR_HEAP_DESC gpuDescHeapDesc = {};
+ gpuDescHeapDesc.NumDescriptors = MAX_GPU_CBVSRVUAV_DESCRIPTORS;
+ gpuDescHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
+ gpuDescHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
+
+ if (FAILED(device->CreateDescriptorHeap(&gpuDescHeapDesc, IID_PPV_ARGS(&gpuCbvSrvUavHeap)))) {
+ qWarning("Failed to create shader-visible CBV-SRV-UAV heap");
+ return;
+ }
+
+ cpuDescHeapManager.initialize(device);
setupRenderTargets();
@@ -515,11 +590,26 @@ void QSGD3D12EnginePrivate::initialize(QWindow *w)
// created in recording state, close it for now
commandList->Close();
- presentFence = createFence();
+ if (FAILED(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_COPY, copyCommandAllocator.Get(),
+ nullptr, IID_PPV_ARGS(&copyCommandList)))) {
+ qWarning("Failed to create copy command list");
+ return;
+ }
+ copyCommandList->Close();
- vertexData = StagingBufferRef();
- indexData = StagingBufferRef();
- constantData = StagingBufferRef();
+ presentFence = createCPUWaitableFence();
+
+ if (FAILED(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&textureUploadFence)))) {
+ qWarning("Failed to create fence");
+ return;
+ }
+
+ vertexData = VICBufferRef();
+ indexData = VICBufferRef();
+ constantData = VICBufferRef();
+
+ psoCache.setMaxCost(MAX_CACHED_PSO);
+ rootSigCache.setMaxCost(MAX_CACHED_ROOTSIG);
initialized = true;
}
@@ -593,13 +683,11 @@ void QSGD3D12EnginePrivate::setupRenderTargets()
qWarning("Failed to get buffer %d from swap chain", i);
return;
}
- D3D12_CPU_DESCRIPTOR_HANDLE h = descHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_RTV).cpu;
- if (i == 0)
- rtv0 = h;
- device->CreateRenderTargetView(renderTargets[i].Get(), nullptr, h);
+ rtv[i] = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
+ device->CreateRenderTargetView(renderTargets[i].Get(), nullptr, rtv[i]);
}
- dsv = descHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_DSV).cpu;
+ dsv = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
ID3D12Resource *ds = createDepthStencil(dsv, window->size(), 0);
if (ds)
depthStencil.Attach(ds);
@@ -615,8 +703,11 @@ void QSGD3D12EnginePrivate::resize()
// Clear these, otherwise resizing will fail.
depthStencil = nullptr;
- for (int i = 0; i < swapChainBufferCount; ++i)
+ cpuDescHeapManager.release(dsv, D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
+ for (int i = 0; i < swapChainBufferCount; ++i) {
renderTargets[i] = nullptr;
+ cpuDescHeapManager.release(rtv[i], D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
+ }
HRESULT hr = swapChain->ResizeBuffers(swapChainBufferCount, window->width(), window->height(), DXGI_FORMAT_R8G8B8A8_UNORM, 0);
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
@@ -641,9 +732,9 @@ void QSGD3D12EnginePrivate::deviceLost()
// all the resources on the next beginFrame().
}
-QSGD3D12Fence *QSGD3D12EnginePrivate::createFence() const
+QSGD3D12CPUWaitableFence *QSGD3D12EnginePrivate::createCPUWaitableFence() const
{
- QSGD3D12Fence *f = new QSGD3D12Fence;
+ QSGD3D12CPUWaitableFence *f = new QSGD3D12CPUWaitableFence;
HRESULT hr = device->CreateFence(f->value, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&f->fence));
if (FAILED(hr)) {
qWarning("Failed to create fence: 0x%x", hr);
@@ -653,7 +744,7 @@ QSGD3D12Fence *QSGD3D12EnginePrivate::createFence() const
return f;
}
-void QSGD3D12EnginePrivate::waitForGPU(QSGD3D12Fence *f) const
+void QSGD3D12EnginePrivate::waitForGPU(QSGD3D12CPUWaitableFence *f) const
{
const UINT64 newValue = f->value.fetchAndAddAcquire(1) + 1;
commandQueue->Signal(f->fence.Get(), newValue);
@@ -699,7 +790,7 @@ ID3D12Resource *QSGD3D12EnginePrivate::createBuffer(int size)
bufDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
HRESULT hr = device->CreateCommittedResource(&uploadHeapProp, D3D12_HEAP_FLAG_NONE, &bufDesc,
- D3D12_RESOURCE_STATE_GENERIC_READ, Q_NULLPTR, IID_PPV_ARGS(&buf));
+ D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&buf));
if (FAILED(hr))
qWarning("Failed to create buffer resource: 0x%x", hr);
@@ -714,13 +805,18 @@ ID3D12Resource *QSGD3D12EnginePrivate::backBufferRT() const
D3D12_CPU_DESCRIPTOR_HANDLE QSGD3D12EnginePrivate::backBufferRTV() const
{
const int frameIndex = swapChain->GetCurrentBackBufferIndex();
- D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtv0;
- rtvHandle.ptr += frameIndex * descHeapManager.handleSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
+ D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtv[0];
+ rtvHandle.ptr += frameIndex * cpuDescHeapManager.handleSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
return rtvHandle;
}
void QSGD3D12EnginePrivate::beginFrame()
{
+ if (inFrame)
+ qWarning("beginFrame called again without an endFrame");
+
+ inFrame = true;
+
if (Q_UNLIKELY(debug_render())) {
static int cnt = 0;
qDebug() << "***** begin frame" << cnt;
@@ -736,11 +832,12 @@ void QSGD3D12EnginePrivate::beginFrame()
transitionResource(backBufferRT(), commandList.Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
- m_drawingMode = QSGGeometry::DrawingMode(-1);
- m_indexBufferSet = false;
+ drawingMode = QSGGeometry::DrawingMode(-1);
+ indexBufferSet = false;
+ cbvSrvUavNextFreeDescriptorIndex = 0;
}
-void QSGD3D12EnginePrivate::updateBuffer(StagingBufferRef *br, ID3D12Resource *r, const char *dbgstr)
+void QSGD3D12EnginePrivate::updateBuffer(VICBufferRef *br, ID3D12Resource *r, const char *dbgstr)
{
quint8 *p = nullptr;
const D3D12_RANGE readRange = { 0, 0 };
@@ -769,6 +866,41 @@ void QSGD3D12EnginePrivate::endFrame()
updateBuffer(&indexData, indexBuffer.Get(), "index");
updateBuffer(&constantData, constantBuffer.Get(), "constant");
+ // Wait for texture uploads (the ones that are needed by this frame) to finish.
+ quint64 topFenceValue = 0;
+ for (uint id : qAsConst(currentFrameTextures)) {
+ const int idx = id - 1;
+ Q_ASSERT(idx < textures.count());
+ const Texture &t(textures[idx]);
+ if (t.fenceValue)
+ topFenceValue = qMax(topFenceValue, t.fenceValue);
+ }
+ if (topFenceValue) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("wait for texture fence %llu", topFenceValue);
+ commandQueue->Wait(textureUploadFence.Get(), topFenceValue);
+ }
+ for (uint id : qAsConst(currentFrameTextures)) {
+ const int idx = id - 1;
+ Texture &t(textures[idx]);
+ if (t.fenceValue) {
+ t.fenceValue = 0;
+ t.stagingBuffer = nullptr;
+ }
+ }
+ if (topFenceValue) {
+ bool hasActiveTextureUpload = false;
+ for (const Texture &t : qAsConst(textures)) {
+ if (t.fenceValue) {
+ hasActiveTextureUpload = true;
+ break;
+ }
+ }
+ if (!hasActiveTextureUpload)
+ copyCommandAllocator->Reset();
+ }
+ currentFrameTextures.clear();
+
transitionResource(backBufferRT(), commandList.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
HRESULT hr = commandList->Close();
@@ -780,23 +912,72 @@ void QSGD3D12EnginePrivate::endFrame()
ID3D12CommandList *commandLists[] = { commandList.Get() };
commandQueue->ExecuteCommandLists(_countof(commandLists), commandLists);
+
+ inFrame = false;
}
+// Root signature:
+// [0] CBV - always present
+// [1] table with 1 SRV per texture (optional)
+// one constant sampler per texture (optional)
+//
+// SRVs can be created freely via QSGD3D12CPUDescriptorHeapManager and stored
+// in QSGD3D12TextureView. The engine will copy them onto a dedicated,
+// shader-visible CBV-SRV-UAV heap.
+
void QSGD3D12EnginePrivate::setPipelineState(const QSGD3D12PipelineState &pipelineState)
{
- // One single root signature for now. This will obviously need some improvements later on...
- if (!m_rootSig) {
- D3D12_ROOT_PARAMETER rootParameter;
- rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
- rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
- rootParameter.Descriptor.ShaderRegister = 0; // b0
- rootParameter.Descriptor.RegisterSpace = 0;
+ RootSigCacheEntry *cachedRootSig = rootSigCache[pipelineState.shaders.rootSig];
+ if (!cachedRootSig) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("NEW ROOTSIG");
+
+ cachedRootSig = new RootSigCacheEntry;
+
+ D3D12_ROOT_PARAMETER rootParams[4];
+ int rootParamCount = 0;
+
+ rootParams[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+ rootParams[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+ rootParams[0].Descriptor.ShaderRegister = 0; // b0
+ rootParams[0].Descriptor.RegisterSpace = 0;
+ ++rootParamCount;
+
+ if (!pipelineState.shaders.rootSig.textureViews.isEmpty()) {
+ rootParams[rootParamCount].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ rootParams[rootParamCount].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
+ rootParams[rootParamCount].DescriptorTable.NumDescriptorRanges = 1;
+ D3D12_DESCRIPTOR_RANGE descRange;
+ descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+ descRange.NumDescriptors = pipelineState.shaders.rootSig.textureViews.count();
+ descRange.BaseShaderRegister = 0; // t0, t1, ...
+ descRange.RegisterSpace = 0;
+ descRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+ rootParams[rootParamCount].DescriptorTable.pDescriptorRanges = &descRange;
+ ++rootParamCount;
+ }
+ Q_ASSERT(rootParamCount <= _countof(rootParams));
D3D12_ROOT_SIGNATURE_DESC desc;
- desc.NumParameters = 1;
- desc.pParameters = &rootParameter;
- desc.NumStaticSamplers = 0;
- desc.pStaticSamplers = nullptr;
+ desc.NumParameters = rootParamCount;
+ desc.pParameters = rootParams;
+ desc.NumStaticSamplers = pipelineState.shaders.rootSig.textureViews.count();
+ D3D12_STATIC_SAMPLER_DESC staticSamplers[8];
+ int sdIdx = 0;
+ Q_ASSERT(pipelineState.shaders.rootSig.textureViews.count() <= _countof(staticSamplers));
+ for (const QSGD3D12TextureView &tv : qAsConst(pipelineState.shaders.rootSig.textureViews)) {
+ D3D12_STATIC_SAMPLER_DESC sd = {};
+ sd.Filter = D3D12_FILTER(tv.filter);
+ sd.AddressU = D3D12_TEXTURE_ADDRESS_MODE(tv.addressModeHoriz);
+ sd.AddressV = D3D12_TEXTURE_ADDRESS_MODE(tv.addressModeVert);
+ sd.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+ sd.MinLOD = 0.0f;
+ sd.MaxLOD = D3D12_FLOAT32_MAX;
+ sd.ShaderRegister = sdIdx; // t0, t1, ...
+ sd.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
+ staticSamplers[sdIdx++] = sd;
+ }
+ desc.pStaticSamplers = staticSamplers;
desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
ComPtr<ID3DBlob> signature;
@@ -806,39 +987,42 @@ void QSGD3D12EnginePrivate::setPipelineState(const QSGD3D12PipelineState &pipeli
return;
}
if (FAILED(device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(),
- IID_PPV_ARGS(&m_rootSig)))) {
+ IID_PPV_ARGS(&cachedRootSig->rootSig)))) {
qWarning("Failed to create root signature");
return;
}
- }
- // Unlimited number of cached PSOs for now, may want to change this later.
- ComPtr<ID3D12PipelineState> pso = m_psoCache[pipelineState];
+ rootSigCache.insert(pipelineState.shaders.rootSig, cachedRootSig);
+ }
- if (!pso) {
+ PSOCacheEntry *cachedPso = psoCache[pipelineState];
+ if (!cachedPso) {
if (Q_UNLIKELY(debug_render()))
qDebug("NEW PSO");
+ cachedPso = new PSOCacheEntry;
+
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
D3D12_INPUT_ELEMENT_DESC inputElements[8];
Q_ASSERT(pipelineState.inputElements.count() <= _countof(inputElements));
- UINT ieIdx = 0;
+ int ieIdx = 0;
for (const QSGD3D12InputElement &ie : pipelineState.inputElements) {
D3D12_INPUT_ELEMENT_DESC ieDesc = {};
- ieDesc.SemanticName = ie.name;
+ ieDesc.SemanticName = ie.semanticName;
+ ieDesc.SemanticIndex = ie.semanticIndex;
ieDesc.Format = DXGI_FORMAT(ie.format);
ieDesc.InputSlot = ie.slot;
ieDesc.AlignedByteOffset = ie.offset;
ieDesc.InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
if (Q_UNLIKELY(debug_render()))
- qDebug("input [%d]: %s %d 0x%x %d", ieIdx, ie.name, ie.offset, ie.format, ie.slot);
+ qDebug("input [%d]: %s %d 0x%x %d", ieIdx, ie.semanticName, ie.offset, ie.format, ie.slot);
inputElements[ieIdx++] = ieDesc;
}
- psoDesc.InputLayout = { inputElements, ieIdx };
+ psoDesc.InputLayout = { inputElements, UINT(ieIdx) };
- psoDesc.pRootSignature = m_rootSig.Get();
+ psoDesc.pRootSignature = cachedRootSig->rootSig.Get();
D3D12_SHADER_BYTECODE vshader;
vshader.pShaderBytecode = pipelineState.shaders.vs;
@@ -899,17 +1083,43 @@ void QSGD3D12EnginePrivate::setPipelineState(const QSGD3D12PipelineState &pipeli
psoDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
psoDesc.SampleDesc.Count = 1;
- HRESULT hr = device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&pso));
+ HRESULT hr = device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&cachedPso->pso));
if (FAILED(hr)) {
qWarning("Failed to create graphics pipeline state");
return;
}
- m_psoCache[pipelineState] = pso;
+
+ psoCache.insert(pipelineState, cachedPso);
}
- commandList->SetPipelineState(pso.Get());
+ commandList->SetPipelineState(cachedPso->pso.Get());
- commandList->SetGraphicsRootSignature(m_rootSig.Get()); // invalidates bindings
+ commandList->SetGraphicsRootSignature(cachedRootSig->rootSig.Get()); // invalidates bindings
+
+ if (!pipelineState.shaders.rootSig.textureViews.isEmpty()) {
+ Q_ASSERT(currentDrawTextures.count() == pipelineState.shaders.rootSig.textureViews.count());
+
+ ID3D12DescriptorHeap *heaps[] = { gpuCbvSrvUavHeap.Get() };
+ commandList->SetDescriptorHeaps(_countof(heaps), heaps);
+
+ // Copy the SRVs to a drawcall-dedicated area of the shader-visible descriptor heap.
+ const uint stride = cpuDescHeapManager.handleSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ D3D12_CPU_DESCRIPTOR_HANDLE dst = gpuCbvSrvUavHeap->GetCPUDescriptorHandleForHeapStart();
+ Q_ASSERT(cbvSrvUavNextFreeDescriptorIndex + currentDrawTextures.count() <= MAX_GPU_CBVSRVUAV_DESCRIPTORS);
+ dst.ptr += cbvSrvUavNextFreeDescriptorIndex * stride;
+ for (uint id : qAsConst(currentDrawTextures)) {
+ const int idx = id - 1;
+ device->CopyDescriptorsSimple(1, dst, textures[idx].srv, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ dst.ptr += stride;
+ }
+
+ D3D12_GPU_DESCRIPTOR_HANDLE gpuAddr = gpuCbvSrvUavHeap->GetGPUDescriptorHandleForHeapStart();
+ gpuAddr.ptr += cbvSrvUavNextFreeDescriptorIndex * stride;
+ commandList->SetGraphicsRootDescriptorTable(1, gpuAddr);
+
+ cbvSrvUavNextFreeDescriptorIndex += currentDrawTextures.count();
+ currentDrawTextures.clear();
+ }
}
void QSGD3D12EnginePrivate::setVertexBuffer(const quint8 *data, int size)
@@ -976,7 +1186,7 @@ void QSGD3D12EnginePrivate::queueSetStencilRef(quint32 ref)
commandList->OMSetStencilRef(ref);
}
-void QSGD3D12EnginePrivate::queueDraw(QSGGeometry::DrawingMode mode, int count, int vboOffset, int vboStride,
+void QSGD3D12EnginePrivate::queueDraw(QSGGeometry::DrawingMode mode, int count, int vboOffset, int vboSize, int vboStride,
int cboOffset,
int startIndexIndex, QSGD3D12Format indexFormat)
{
@@ -1035,15 +1245,9 @@ void QSGD3D12EnginePrivate::queueDraw(QSGGeometry::DrawingMode mode, int count,
commandList->SetGraphicsRootConstantBufferView(0, constantBuffer->GetGPUVirtualAddress() + cboOffset);
Q_ASSERT(vertexBuffer);
-
- D3D12_VERTEX_BUFFER_VIEW vbv;
- vbv.BufferLocation = vertexBuffer->GetGPUVirtualAddress() + vboOffset;
- vbv.SizeInBytes = vboStride * count;
- vbv.StrideInBytes = vboStride;
-
Q_ASSERT(indexBuffer || startIndexIndex < 0);
- if (mode != m_drawingMode) {
+ if (mode != drawingMode) {
D3D_PRIMITIVE_TOPOLOGY topology;
switch (mode) {
case QSGGeometry::DrawPoints:
@@ -1066,14 +1270,19 @@ void QSGD3D12EnginePrivate::queueDraw(QSGGeometry::DrawingMode mode, int count,
break;
}
commandList->IASetPrimitiveTopology(topology);
- m_drawingMode = mode;
+ drawingMode = mode;
}
+ D3D12_VERTEX_BUFFER_VIEW vbv;
+ vbv.BufferLocation = vertexBuffer->GetGPUVirtualAddress() + vboOffset;
+ vbv.SizeInBytes = vboSize;
+ vbv.StrideInBytes = vboStride;
+
// must be set after the topology
commandList->IASetVertexBuffers(0, 1, &vbv);
- if (startIndexIndex >= 0 && !m_indexBufferSet) {
- m_indexBufferSet = true;
+ if (startIndexIndex >= 0 && !indexBufferSet) {
+ indexBufferSet = true;
D3D12_INDEX_BUFFER_VIEW ibv;
ibv.BufferLocation = indexBuffer->GetGPUVirtualAddress();
ibv.SizeInBytes = indexData.size;
@@ -1116,4 +1325,167 @@ void QSGD3D12EnginePrivate::waitGPU()
waitForGPU(presentFence);
}
+uint QSGD3D12EnginePrivate::createTexture(QImage::Format format, const QSize &size, QSGD3D12Engine::TextureCreateFlags flags)
+{
+ int id = 0;
+ for (int i = 0; i < textures.count(); ++i) {
+ if (!textures[i].texture) {
+ id = i + 1;
+ break;
+ }
+ }
+ if (!id) {
+ textures.resize(textures.size() + 1);
+ id = textures.count();
+ }
+
+ const int idx = id - 1;
+ Texture &t(textures[idx]);
+
+ D3D12_HEAP_PROPERTIES defaultHeapProp = {};
+ defaultHeapProp.Type = D3D12_HEAP_TYPE_DEFAULT;
+
+ D3D12_RESOURCE_DESC textureDesc = {};
+ textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ textureDesc.Width = size.width();
+ textureDesc.Height = size.height();
+ textureDesc.DepthOrArraySize = 1;
+ textureDesc.MipLevels = 1; // ###
+ textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // ### use format
+ textureDesc.SampleDesc.Count = 1;
+ textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+
+ HRESULT hr = device->CreateCommittedResource(&defaultHeapProp, D3D12_HEAP_FLAG_NONE, &textureDesc,
+ D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(&t.texture));
+ if (FAILED(hr)) {
+ qWarning("Failed to create texture resource: 0x%x", hr);
+ return 0;
+ }
+
+ t.srv = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+
+ D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
+ srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+ srvDesc.Format = textureDesc.Format;
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+ srvDesc.Texture2D.MipLevels = 1; // ###
+
+ device->CreateShaderResourceView(t.texture.Get(), &srvDesc, t.srv);
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("allocated texture %d", id);
+
+ return id;
+}
+
+void QSGD3D12EnginePrivate::releaseTexture(uint id)
+{
+ if (!id)
+ return;
+
+ const int idx = id - 1;
+ Q_ASSERT(idx < textures.count());
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("releasing texture %d", id);
+
+ textures[idx].texture = nullptr;
+ cpuDescHeapManager.release(textures[idx].srv, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+}
+
+SIZE_T QSGD3D12EnginePrivate::textureSRV(uint id) const
+{
+ Q_ASSERT(id);
+ const int idx = id - 1;
+ Q_ASSERT(idx < textures.count());
+ return textures[idx].srv.ptr;
+}
+
+void QSGD3D12EnginePrivate::queueTextureUpload(uint id, const QImage &image, QSGD3D12Engine::TextureUploadFlags flags)
+{
+ Q_ASSERT(id);
+ const int idx = id - 1;
+ Q_ASSERT(idx < textures.count());
+
+ Texture &t(textures[idx]);
+ if (t.fenceValue) {
+ qWarning("queueTextureUpload: An upload is still active for texture %d", id);
+ return;
+ }
+ if (!t.texture) {
+ qWarning("queueTextureUpload: Attempted to upload for non-created texture %d", id);
+ return;
+ }
+
+ t.fenceValue = nextTextureUploadFenceValue.fetchAndAddAcquire(1) + 1;
+
+ D3D12_RESOURCE_DESC textureDesc = t.texture->GetDesc();
+ UINT64 bufferSize;
+ const int TEXTURE_MIP_LEVELS = 1; // ###
+ D3D12_PLACED_SUBRESOURCE_FOOTPRINT textureLayout[TEXTURE_MIP_LEVELS];
+ device->GetCopyableFootprints(&textureDesc, 0, TEXTURE_MIP_LEVELS, 0, textureLayout, nullptr, nullptr, &bufferSize);
+
+ D3D12_RESOURCE_DESC bufDesc = {};
+ bufDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+ bufDesc.Width = bufferSize;
+ bufDesc.Height = 1;
+ bufDesc.DepthOrArraySize = 1;
+ bufDesc.MipLevels = 1;
+ bufDesc.Format = DXGI_FORMAT_UNKNOWN;
+ bufDesc.SampleDesc.Count = 1;
+ bufDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+
+ D3D12_HEAP_PROPERTIES uploadHeapProp = {};
+ uploadHeapProp.Type = D3D12_HEAP_TYPE_UPLOAD;
+
+ if (FAILED(device->CreateCommittedResource(&uploadHeapProp, D3D12_HEAP_FLAG_NONE, &bufDesc,
+ D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&t.stagingBuffer)))) {
+ qWarning("Failed to create texture upload buffer");
+ return;
+ }
+
+ QImage convImage = image.convertToFormat(QImage::Format_RGBA8888); // ###
+
+ quint8 *p = nullptr;
+ const D3D12_RANGE readRange = { 0, 0 };
+ if (FAILED(t.stagingBuffer->Map(0, &readRange, reinterpret_cast<void **>(&p)))) {
+ qWarning("Map failed (texture upload buffer)");
+ return;
+ }
+ quint8 *lp = p + textureLayout[0].Offset;
+ for (uint y = 0; y < textureDesc.Height; ++y) {
+ memcpy(lp, convImage.scanLine(y), convImage.width() * 4);
+ lp += textureLayout[0].Footprint.RowPitch;
+ }
+ t.stagingBuffer->Unmap(0, nullptr);
+
+ copyCommandList->Reset(copyCommandAllocator.Get(), nullptr);
+
+ D3D12_TEXTURE_COPY_LOCATION dstLoc;
+ dstLoc.pResource = t.texture.Get();
+ dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ dstLoc.SubresourceIndex = 0;
+ D3D12_TEXTURE_COPY_LOCATION srcLoc;
+ srcLoc.pResource = t.stagingBuffer.Get();
+ srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
+ srcLoc.PlacedFootprint = textureLayout[0];
+ copyCommandList->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, nullptr);
+
+ copyCommandList->Close();
+ ID3D12CommandList *commandLists[] = { copyCommandList.Get() };
+ copyCommandQueue->ExecuteCommandLists(_countof(commandLists), commandLists);
+ copyCommandQueue->Signal(textureUploadFence.Get(), t.fenceValue);
+}
+
+void QSGD3D12EnginePrivate::addFrameTextureDep(uint id)
+{
+ if (!inFrame) {
+ qWarning("addFrameTextureDep cannot be called outside begin/endFrame");
+ return;
+ }
+
+ currentFrameTextures.insert(id);
+ currentDrawTextures.append(id);
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h
index a03be4b884..a6edf7698d 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h
@@ -62,7 +62,7 @@ QT_BEGIN_NAMESPACE
class QSGD3D12EnginePrivate;
// Shader bytecode and other strings are expected to be static so that a
-// different pointer == different shader.
+// different pointer means a different shader.
enum QSGD3D12Format {
FmtUnknown = 0,
@@ -84,26 +84,69 @@ enum QSGD3D12Format {
struct QSGD3D12InputElement
{
- QSGD3D12InputElement() { }
- QSGD3D12InputElement(const char *name, QSGD3D12Format format, quint32 slot, quint32 offset)
- : name(name), format(format), slot(slot), offset(offset) { }
-
- const char *name = nullptr;
+ const char *semanticName = nullptr;
+ int semanticIndex = 0;
QSGD3D12Format format = FmtFloat4;
quint32 slot = 0;
quint32 offset = 0;
bool operator==(const QSGD3D12InputElement &other) const {
- return name == other.name && format == other.format
- && slot == other.slot && offset == other.offset;
+ return semanticName == other.semanticName && semanticIndex == other.semanticIndex
+ && format == other.format && slot == other.slot && offset == other.offset;
}
};
inline uint qHash(const QSGD3D12InputElement &key, uint seed = 0)
{
- return qHash(key.name, seed) + key.format + key.offset;
+ return qHash(key.semanticName, seed) + key.semanticIndex + key.format + key.offset;
+}
+
+struct QSGD3D12TextureView
+{
+ enum Filter {
+ FilterNearest = 0,
+ FilterLinear = 0x15,
+ FilterMinMagNearestMipLinear = 0x1,
+ FilterMinMagLinearMipNearest = 0x14
+ };
+
+ enum AddressMode {
+ AddressWrap = 1,
+ AddressClamp = 3
+ };
+
+ Filter filter = FilterLinear;
+ AddressMode addressModeHoriz = AddressClamp;
+ AddressMode addressModeVert = AddressClamp;
+
+ bool operator==(const QSGD3D12TextureView &other) const {
+ return filter == other.filter
+ && addressModeHoriz == other.addressModeHoriz
+ && addressModeVert == other.addressModeVert;
+ }
+};
+
+inline uint qHash(const QSGD3D12TextureView &key, uint seed = 0)
+{
+ Q_UNUSED(seed);
+ return key.filter + key.addressModeHoriz + key.addressModeVert;
}
+struct QSGD3D12RootSignature
+{
+ QVector<QSGD3D12TextureView> textureViews;
+
+ bool operator==(const QSGD3D12RootSignature &other) const {
+ return textureViews == other.textureViews;
+ }
+};
+
+inline uint qHash(const QSGD3D12RootSignature &key, uint seed = 0)
+{
+ return qHash(key.textureViews, seed);
+}
+
+// Shader bytecode blobs and root signature-related data.
struct QSGD3D12ShaderState
{
const quint8 *vs = nullptr;
@@ -111,15 +154,18 @@ struct QSGD3D12ShaderState
const quint8 *ps = nullptr;
quint32 psSize = 0;
+ QSGD3D12RootSignature rootSig;
+
bool operator==(const QSGD3D12ShaderState &other) const {
return vs == other.vs && vsSize == other.vsSize
- && ps == other.ps && psSize == other.psSize;
+ && ps == other.ps && psSize == other.psSize
+ && rootSig == other.rootSig;
}
};
inline uint qHash(const QSGD3D12ShaderState &key, uint seed = 0)
{
- return qHash(key.vs, seed) + key.vsSize + qHash(key.ps, seed) + key.psSize;
+ return qHash(key.vs, seed) + key.vsSize + qHash(key.ps, seed) + key.psSize + qHash(key.rootSig, seed);
}
struct QSGD3D12PipelineState
@@ -237,7 +283,8 @@ public:
void queueClearDepthStencil(float depthValue, quint8 stencilValue, ClearFlags which);
void queueSetStencilRef(quint32 ref);
- void queueDraw(QSGGeometry::DrawingMode mode, int count, int vboOffset, int vboStride,
+ void queueDraw(QSGGeometry::DrawingMode mode, int count,
+ int vboOffset, int vboSize, int vboStride,
int cboOffset,
int startIndexIndex = -1, QSGD3D12Format indexFormat = FmtUnsignedShort);
@@ -247,12 +294,31 @@ public:
static quint32 alignedConstantBufferSize(quint32 size);
static QSGD3D12Format toDXGIFormat(QSGGeometry::Type sgtype, int tupleSize = 1, int *size = nullptr);
+ enum TextureCreateFlag {
+ CreateWithAlpha = 0x1,
+ CreateWithMipMaps = 0x2
+ };
+ Q_DECLARE_FLAGS(TextureCreateFlags, TextureCreateFlag)
+
+ enum TextureUploadFlag {
+ UploadWithMipMaps = 0x1
+ };
+ Q_DECLARE_FLAGS(TextureUploadFlags, TextureUploadFlag)
+
+ uint createTexture(QImage::Format format, const QSize &size, TextureCreateFlags flags);
+ void releaseTexture(uint id);
+ SIZE_T textureSRV(uint id) const;
+ void queueTextureUpload(uint id, const QImage &image, TextureUploadFlags flags);
+ void addFrameTextureDep(uint id);
+
private:
QSGD3D12EnginePrivate *d;
Q_DISABLE_COPY(QSGD3D12Engine)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Engine::ClearFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Engine::TextureCreateFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Engine::TextureUploadFlags)
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h
index 6dd95e725e..ba2bededd5 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h
@@ -52,6 +52,7 @@
//
#include "qsgd3d12engine_p.h"
+#include <QCache>
#include <d3d12.h>
#include <dxgi1_4.h>
@@ -62,28 +63,22 @@ using namespace Microsoft::WRL;
// No moc-related features (Q_OBJECT, signals, etc.) can be used here to due
// moc-generated code failing to compile when combined with COM stuff.
-QT_BEGIN_NAMESPACE
+// Recommended reading before moving further: https://github.com/Microsoft/DirectXTK/wiki/ComPtr
+// Note esp. operator= vs. Attach and operator& vs. GetAddressOf
-struct QSGD3D12DescriptorHandle
-{
- QSGD3D12DescriptorHandle() { cpu.ptr = 0; gpu.ptr = 0; }
- D3D12_CPU_DESCRIPTOR_HANDLE cpu;
- D3D12_GPU_DESCRIPTOR_HANDLE gpu;
-};
+// ID3D12* is never passed to Qt containers directly. Always use ComPtr and put it into a struct.
-class QSGD3D12DescriptorHeapManager
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12CPUDescriptorHeapManager
{
public:
void initialize(ID3D12Device *device);
void releaseResources();
- enum Flag {
- ShaderVisible = 0x01
- };
- Q_DECLARE_FLAGS(Flags, Flag)
-
- QSGD3D12DescriptorHandle allocate(D3D12_DESCRIPTOR_HEAP_TYPE type, Flags flags = 0);
+ D3D12_CPU_DESCRIPTOR_HANDLE allocate(D3D12_DESCRIPTOR_HEAP_TYPE type);
+ void release(D3D12_CPU_DESCRIPTOR_HANDLE handle, D3D12_DESCRIPTOR_HEAP_TYPE type);
quint32 handleSize(D3D12_DESCRIPTOR_HEAP_TYPE type) const { return m_handleSizes[type]; }
private:
@@ -91,9 +86,9 @@ private:
struct Heap {
D3D12_DESCRIPTOR_HEAP_TYPE type;
ComPtr<ID3D12DescriptorHeap> heap;
- int count = 0;
- QSGD3D12DescriptorHandle start;
+ D3D12_CPU_DESCRIPTOR_HANDLE start;
quint32 handleSize;
+ quint32 freeMap[8];
};
QVector<Heap> m_heaps;
quint32 m_handleSizes[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES];
@@ -121,11 +116,9 @@ private:
QVector<DeviceLossObserver *> m_observers;
};
-Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12DescriptorHeapManager::Flags)
-
-struct QSGD3D12Fence
+struct QSGD3D12CPUWaitableFence
{
- ~QSGD3D12Fence() {
+ ~QSGD3D12CPUWaitableFence() {
if (event)
CloseHandle(event);
}
@@ -159,13 +152,20 @@ public:
void queueClearDepthStencil(float depthValue, quint8 stencilValue, QSGD3D12Engine::ClearFlags which);
void queueSetStencilRef(quint32 ref);
- void queueDraw(QSGGeometry::DrawingMode mode, int count, int vboOffset, int vboStride,
+ void queueDraw(QSGGeometry::DrawingMode mode, int count,
+ int vboOffset, int vboSize, int vboStride,
int cboOffset,
int startIndexIndex, QSGD3D12Format indexFormat);
void present();
void waitGPU();
+ uint createTexture(QImage::Format format, const QSize &size, QSGD3D12Engine::TextureCreateFlags flags);
+ void releaseTexture(uint id);
+ SIZE_T textureSRV(uint id) const;
+ void queueTextureUpload(uint id, const QImage &image, QSGD3D12Engine::TextureUploadFlags flags);
+ void addFrameTextureDep(uint id);
+
// the device is intentionally hidden here. all resources have to go
// through the engine and, unlike with GL, cannot just be created in random
// places due to the need for proper tracking, managing and releasing.
@@ -176,8 +176,8 @@ private:
DXGI_SAMPLE_DESC makeSampleDesc(DXGI_FORMAT format, int samples);
ID3D12Resource *createDepthStencil(D3D12_CPU_DESCRIPTOR_HANDLE viewHandle, const QSize &size, int samples);
- QSGD3D12Fence *createFence() const;
- void waitForGPU(QSGD3D12Fence *f) const;
+ QSGD3D12CPUWaitableFence *createCPUWaitableFence() const;
+ void waitForGPU(QSGD3D12CPUWaitableFence *f) const;
void transitionResource(ID3D12Resource *resource, ID3D12GraphicsCommandList *commandList,
D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after) const;
@@ -187,46 +187,70 @@ private:
ID3D12Resource *backBufferRT() const;
D3D12_CPU_DESCRIPTOR_HANDLE backBufferRTV() const;
- struct StagingBufferRef {
+ struct VICBufferRef {
const quint8 *p = nullptr;
int size = 0;
bool fullChange = true;
QVector<QPair<int, int> > dirty;
- StagingBufferRef() { dirty.reserve(256); }
+ VICBufferRef() { dirty.reserve(256); }
};
- void updateBuffer(StagingBufferRef *br, ID3D12Resource *r, const char *dbgstr);
+ void updateBuffer(VICBufferRef *br, ID3D12Resource *r, const char *dbgstr);
bool initialized = false;
+ bool inFrame = false;
QWindow *window = nullptr;
int swapChainBufferCount = 2;
ID3D12Device *device;
ComPtr<ID3D12CommandQueue> commandQueue;
+ ComPtr<ID3D12CommandQueue> copyCommandQueue;
ComPtr<IDXGISwapChain3> swapChain;
ComPtr<ID3D12Resource> renderTargets[2];
- D3D12_CPU_DESCRIPTOR_HANDLE rtv0;
+ D3D12_CPU_DESCRIPTOR_HANDLE rtv[2];
D3D12_CPU_DESCRIPTOR_HANDLE dsv;
ComPtr<ID3D12Resource> depthStencil;
ComPtr<ID3D12CommandAllocator> commandAllocator;
- ComPtr<ID3D12CommandAllocator> bundleAllocator;
+ ComPtr<ID3D12CommandAllocator> copyCommandAllocator;
ComPtr<ID3D12GraphicsCommandList> commandList;
- QSGD3D12DescriptorHeapManager descHeapManager;
- QSGD3D12Fence *presentFence = nullptr;
+ ComPtr<ID3D12GraphicsCommandList> copyCommandList;
+ QSGD3D12CPUDescriptorHeapManager cpuDescHeapManager;
+ QSGD3D12CPUWaitableFence *presentFence = nullptr;
- StagingBufferRef vertexData;
- StagingBufferRef indexData;
- StagingBufferRef constantData;
+ VICBufferRef vertexData;
+ VICBufferRef indexData;
+ VICBufferRef constantData;
ComPtr<ID3D12Resource> vertexBuffer;
ComPtr<ID3D12Resource> indexBuffer;
ComPtr<ID3D12Resource> constantBuffer;
- QHash<QSGD3D12PipelineState, ComPtr<ID3D12PipelineState> > m_psoCache;
+ struct PSOCacheEntry {
+ ComPtr<ID3D12PipelineState> pso;
+ };
+ QCache<QSGD3D12PipelineState, PSOCacheEntry> psoCache;
+ struct RootSigCacheEntry {
+ ComPtr<ID3D12RootSignature> rootSig;
+ };
+ QCache<QSGD3D12RootSignature, RootSigCacheEntry> rootSigCache;
+
+ QSGGeometry::DrawingMode drawingMode;
+ bool indexBufferSet;
+
+ struct Texture {
+ ComPtr<ID3D12Resource> texture;
+ D3D12_CPU_DESCRIPTOR_HANDLE srv;
+ quint64 fenceValue = 0;
+ ComPtr<ID3D12Resource> stagingBuffer;
+ };
- ComPtr<ID3D12RootSignature> m_rootSig;
+ QVector<Texture> textures;
+ ComPtr<ID3D12Fence> textureUploadFence;
+ QAtomicInt nextTextureUploadFenceValue;
+ QSet<uint> currentFrameTextures;
+ QVector<uint> currentDrawTextures;
- QSGGeometry::DrawingMode m_drawingMode;
- bool m_indexBufferSet;
+ ComPtr<ID3D12DescriptorHeap> gpuCbvSrvUavHeap;
+ int cbvSrvUavNextFreeDescriptorIndex;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12imagenode.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12imagenode.cpp
new file mode 100644
index 0000000000..8fccced5f5
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12imagenode.cpp
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12imagenode_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGD3D12ImageNode::QSGD3D12ImageNode()
+{
+ setMaterial(&m_material);
+}
+
+void QSGD3D12ImageNode::setFiltering(QSGTexture::Filtering filtering)
+{
+ if (m_material.filtering() == filtering)
+ return;
+
+ m_material.setFiltering(filtering);
+ //m_smoothMaterial.setFiltering(filtering);
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12ImageNode::setMipmapFiltering(QSGTexture::Filtering filtering)
+{
+ if (m_material.mipmapFiltering() == filtering)
+ return;
+
+ m_material.setMipmapFiltering(filtering);
+ //m_smoothMaterial.setMipmapFiltering(filtering);
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12ImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode)
+{
+ if (m_material.verticalWrapMode() == wrapMode)
+ return;
+
+ m_material.setVerticalWrapMode(wrapMode);
+ //m_smoothMaterial.setVerticalWrapMode(wrapMode);
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12ImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode)
+{
+ if (m_material.horizontalWrapMode() == wrapMode)
+ return;
+
+ m_material.setHorizontalWrapMode(wrapMode);
+ //m_smoothMaterial.setHorizontalWrapMode(wrapMode);
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12ImageNode::updateMaterialAntialiasing()
+{
+ //setMaterial(m_antialiasing ? &m_smoothMaterial : &m_material);
+}
+
+void QSGD3D12ImageNode::setMaterialTexture(QSGTexture *texture)
+{
+ m_material.setTexture(texture);
+// m_smoothMaterial.setTexture(texture);
+}
+
+QSGTexture *QSGD3D12ImageNode::materialTexture() const
+{
+ return m_material.texture();
+}
+
+bool QSGD3D12ImageNode::updateMaterialBlending()
+{
+ const bool alpha = m_material.flags() & QSGMaterial::Blending;
+ if (materialTexture() && alpha != materialTexture()->hasAlphaChannel()) {
+ m_material.setFlag(QSGMaterial::Blending, !alpha);
+ return true;
+ }
+ return false;
+}
+
+bool QSGD3D12ImageNode::supportsWrap(const QSize &) const
+{
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12imagenode_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12imagenode_p.h
new file mode 100644
index 0000000000..3bf2fa13c7
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12imagenode_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12IMAGENODE_P_H
+#define QSGD3D12IMAGENODE_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 <private/qsgdefaultimagenode_p.h>
+#include "qsgd3d12material_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12ImageNode : public QSGDefaultNoMaterialImageNode
+{
+public:
+ QSGD3D12ImageNode();
+
+ void setMipmapFiltering(QSGTexture::Filtering filtering) override;
+ void setFiltering(QSGTexture::Filtering filtering) override;
+ void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) override;
+ void setVerticalWrapMode(QSGTexture::WrapMode wrapMode) override;
+
+ void updateMaterialAntialiasing() override;
+ void setMaterialTexture(QSGTexture *texture) override;
+ QSGTexture *materialTexture() const override;
+ bool updateMaterialBlending() override;
+ bool supportsWrap(const QSize &size) const override;
+
+private:
+ QSGD3D12TextureMaterial m_material;
+// QSGSmoothTextureMaterial m_smoothMaterial;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12IMAGENODE_P_H
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12material.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12material.cpp
index 69160fa6b2..9480c296a3 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12material.cpp
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12material.cpp
@@ -38,12 +38,15 @@
****************************************************************************/
#include "qsgd3d12material_p.h"
+#include "qsgd3d12texture_p.h"
#include <private/qsgrenderer_p.h>
#include "vs_vertexcolor.hlslh"
#include "ps_vertexcolor.hlslh"
#include "vs_smoothcolor.hlslh"
#include "ps_smoothcolor.hlslh"
+#include "vs_texture.hlslh"
+#include "ps_texture.hlslh"
QT_BEGIN_NAMESPACE
@@ -229,4 +232,80 @@ QSGD3D12Material::UpdateResults QSGD3D12SmoothColorMaterial::updatePipeline(cons
return r;
}
+QSGMaterialType QSGD3D12TextureMaterial::mtype;
+
+QSGMaterialType *QSGD3D12TextureMaterial::type() const
+{
+ return &QSGD3D12TextureMaterial::mtype;
+}
+
+int QSGD3D12TextureMaterial::compare(const QSGMaterial *other) const
+{
+ Q_ASSERT(other && type() == other->type());
+ const QSGD3D12TextureMaterial *o = static_cast<const QSGD3D12TextureMaterial *>(other);
+ if (int diff = m_texture->textureId() - o->texture()->textureId())
+ return diff;
+ return int(m_filtering) - int(o->m_filtering);
+}
+
+static const int TEXTURE_CB_SIZE_0 = 16 * sizeof(float); // float4x4
+static const int TEXTURE_CB_SIZE_1 = sizeof(float); // float
+static const int TEXTURE_CB_SIZE = TEXTURE_CB_SIZE_0 + TEXTURE_CB_SIZE_1;
+
+int QSGD3D12TextureMaterial::constantBufferSize() const
+{
+ return QSGD3D12Engine::alignedConstantBufferSize(TEXTURE_CB_SIZE);
+}
+
+void QSGD3D12TextureMaterial::preparePipeline(QSGD3D12ShaderState *shaders)
+{
+ shaders->vs = g_VS_Texture;
+ shaders->vsSize = sizeof(g_VS_Texture);
+ shaders->ps = g_PS_Texture;
+ shaders->psSize = sizeof(g_PS_Texture);
+
+ shaders->rootSig.textureViews.resize(1);
+}
+
+QSGD3D12Material::UpdateResults QSGD3D12TextureMaterial::updatePipeline(const RenderState &state,
+ QSGD3D12ShaderState *shaders,
+ quint8 *constantBuffer)
+{
+ QSGD3D12Material::UpdateResults r = 0;
+ quint8 *p = constantBuffer;
+
+ if (state.isMatrixDirty()) {
+ memcpy(p, state.combinedMatrix().constData(), TEXTURE_CB_SIZE_0);
+ r |= UpdatedConstantBuffer;
+ }
+ p += TEXTURE_CB_SIZE_0;
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(p, &opacity, TEXTURE_CB_SIZE_1);
+ r |= UpdatedConstantBuffer;
+ }
+
+ Q_ASSERT(m_texture);
+ m_texture->setFiltering(m_filtering);
+ m_texture->setMipmapFiltering(m_mipmap_filtering);
+ m_texture->setHorizontalWrapMode(m_horizontal_wrap);
+ m_texture->setVerticalWrapMode(m_vertical_wrap);
+
+ // ### filling this every time is cheap but we could still skip it if we could access the QSGTexture's wrapChanged and similar fields
+ QSGD3D12TextureView &tv(shaders->rootSig.textureViews[0]);
+ if (m_filtering == QSGTexture::Linear)
+ tv.filter = m_mipmap_filtering == QSGTexture::Linear
+ ? QSGD3D12TextureView::FilterLinear : QSGD3D12TextureView::FilterMinMagLinearMipNearest;
+ else
+ tv.filter = m_mipmap_filtering == QSGTexture::Linear
+ ? QSGD3D12TextureView::FilterMinMagNearestMipLinear : QSGD3D12TextureView::FilterNearest;
+ tv.addressModeHoriz = m_horizontal_wrap == QSGTexture::ClampToEdge ? QSGD3D12TextureView::AddressClamp : QSGD3D12TextureView::AddressWrap;
+ tv.addressModeVert = m_vertical_wrap == QSGTexture::ClampToEdge ? QSGD3D12TextureView::AddressClamp : QSGD3D12TextureView::AddressWrap;
+
+ m_texture->bind();
+
+ return r;
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12material_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12material_p.h
index 8821950f32..4509c54a4d 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12material_p.h
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12material_p.h
@@ -52,6 +52,7 @@
//
#include <QtQuick/qsgmaterial.h>
+#include <QtQuick/qsgtexture.h>
#include "qsgd3d12engine_p.h"
QT_BEGIN_NAMESPACE
@@ -145,6 +146,43 @@ private:
static QSGMaterialType mtype;
};
+class QSGD3D12TextureMaterial : public QSGD3D12Material
+{
+public:
+ QSGMaterialType *type() const override;
+ int compare(const QSGMaterial *other) const override;
+
+ virtual int constantBufferSize() const override;
+ void preparePipeline(QSGD3D12ShaderState *shaders) override;
+ UpdateResults updatePipeline(const RenderState &state,
+ QSGD3D12ShaderState *shaders,
+ quint8 *constantBuffer) override;
+
+ void setTexture(QSGTexture *texture) { m_texture = texture; }
+ QSGTexture *texture() const { return m_texture; }
+
+ void setMipmapFiltering(QSGTexture::Filtering filter) { m_mipmap_filtering = filter; }
+ QSGTexture::Filtering mipmapFiltering() const { return m_mipmap_filtering; }
+
+ void setFiltering(QSGTexture::Filtering filter) { m_filtering = filter; }
+ QSGTexture::Filtering filtering() const { return m_filtering; }
+
+ void setHorizontalWrapMode(QSGTexture::WrapMode hwrap) { m_horizontal_wrap = hwrap; }
+ QSGTexture::WrapMode horizontalWrapMode() const { return m_horizontal_wrap; }
+
+ void setVerticalWrapMode(QSGTexture::WrapMode vwrap) { m_vertical_wrap = vwrap; }
+ QSGTexture::WrapMode verticalWrapMode() const { return m_vertical_wrap; }
+
+private:
+ static QSGMaterialType mtype;
+
+ QSGTexture *m_texture = nullptr;
+ QSGTexture::Filtering m_filtering = QSGTexture::Nearest;
+ QSGTexture::Filtering m_mipmap_filtering = QSGTexture::None;
+ QSGTexture::WrapMode m_horizontal_wrap = QSGTexture::ClampToEdge;
+ QSGTexture::WrapMode m_vertical_wrap = QSGTexture::ClampToEdge;
+};
+
QT_END_NAMESPACE
#endif // QSGD3D12MATERIAL_P_H
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext.cpp
index 4c002fcc6a..306a54901a 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext.cpp
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext.cpp
@@ -39,6 +39,7 @@
#include "qsgd3d12rendercontext_p.h"
#include "qsgd3d12renderer_p.h"
+#include "qsgd3d12texture_p.h"
QT_BEGIN_NAMESPACE
@@ -52,17 +53,12 @@ void QSGD3D12RenderContext::initialize(QOpenGLContext *)
Q_UNREACHABLE();
}
-void QSGD3D12RenderContext::invalidate()
-{
- QSGRenderContext::invalidate();
-}
-
QSGTexture *QSGD3D12RenderContext::createTexture(const QImage &image, uint flags) const
{
- Q_UNUSED(image);
- Q_UNUSED(flags);
- Q_UNREACHABLE();
- return nullptr;
+ Q_ASSERT(m_engine);
+ QSGD3D12Texture *t = new QSGD3D12Texture(m_engine);
+ t->setImage(image, flags);
+ return t;
}
QSGRenderer *QSGD3D12RenderContext::createRenderer()
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext_p.h
index 2e05632ad9..ec3bf81288 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext_p.h
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext_p.h
@@ -63,7 +63,6 @@ public:
QSGD3D12RenderContext(QSGContext *ctx);
void initialize(QOpenGLContext *) override; // not in use
- void invalidate() override;
void renderNextFrame(QSGRenderer *renderer, GLuint fbo) override;
QSGTexture *createTexture(const QImage &image, uint flags) const override;
QSGRenderer *createRenderer() override;
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp
index 6d0910c5ae..718dc80804 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp
@@ -439,8 +439,10 @@ void QSGD3D12Renderer::renderElement(int elementIndex)
const QSGGeometry *g = gn->geometry();
QSGD3D12Material *m = static_cast<QSGD3D12Material *>(gn->activeMaterial());
- if (m->type() != m_lastMaterialType)
+ if (m->type() != m_lastMaterialType) {
+ m_pipelineState.shaders.rootSig.textureViews.clear();
m->preparePipeline(&m_pipelineState.shaders);
+ }
QSGD3D12Material::RenderState::DirtyStates dirtyState = m_nodeDirtyMap.value(e.node);
@@ -466,9 +468,6 @@ void QSGD3D12Renderer::renderElement(int elementIndex)
QSGD3D12Material::UpdateResults updRes = m->updatePipeline(QSGD3D12Material::makeRenderState(this, dirtyState),
&m_pipelineState.shaders,
cboPtr);
- // For now there is no way to have extra SRVs and such. Once texturing is
- // introduced, the above update call will have to be able to affect the
- // root signature and communicate the need for SRVs or UAVs to the engine.
if (updRes.testFlag(QSGD3D12Material::UpdatedConstantBuffer))
m_engine->markConstantBufferDirty(e.cboOffset, e.cboSize);
@@ -500,10 +499,12 @@ void QSGD3D12Renderer::setInputLayout(const QSGGeometry *g, QSGD3D12PipelineStat
quint32 offset = 0;
for (int i = 0; i < g->attributeCount(); ++i) {
QSGD3D12InputElement &ie(pipelineState->inputElements[i]);
- static const char *semanticNames[] = { "UNKNOWN", "POSITION", "COLOR", "TEXCOORD" };
+ static const char *semanticNames[] = { "UNKNOWN", "POSITION", "COLOR", "TEXCOORD", "TEXCOORD", "TEXCOORD" };
+ static const int semanticIndices[] = { 0, 0, 0, 0, 1, 2 };
Q_ASSERT(attrs[i].semantic >= 1 && attrs[i].semantic < _countof(semanticNames));
const int tupleSize = attrs[i].tupleSize;
- ie.name = semanticNames[attrs[i].semantic];
+ ie.semanticName = semanticNames[attrs[i].semantic];
+ ie.semanticIndex = semanticIndices[attrs[i].semantic];
ie.offset = offset;
int bytesPerTuple = 0;
ie.format = QSGD3D12Engine::toDXGIFormat(QSGGeometry::Type(attrs[i].type), tupleSize, &bytesPerTuple);
@@ -522,11 +523,13 @@ void QSGD3D12Renderer::queueDrawCall(const QSGGeometry *g, const QSGD3D12Rendere
const QSGD3D12Format indexFormat = QSGD3D12Engine::toDXGIFormat(indexType);
if (indexFormat == FmtUnknown)
qFatal("QSGD3D12Renderer: unsupported index type 0x%x", indexType);
- m_engine->queueDraw(QSGGeometry::DrawingMode(g->drawingMode()), g->indexCount(), e.vboOffset, g->sizeOfVertex(),
+ m_engine->queueDraw(QSGGeometry::DrawingMode(g->drawingMode()), g->indexCount(),
+ e.vboOffset, g->vertexCount() * g->sizeOfVertex(), g->sizeOfVertex(),
e.cboOffset,
e.iboOffset / e.iboStride, indexFormat);
} else {
- m_engine->queueDraw(QSGGeometry::DrawingMode(g->drawingMode()), g->vertexCount(), e.vboOffset, g->sizeOfVertex(),
+ m_engine->queueDraw(QSGGeometry::DrawingMode(g->drawingMode()), g->vertexCount(),
+ e.vboOffset, g->vertexCount() * g->sizeOfVertex(), g->sizeOfVertex(),
e.cboOffset);
}
}
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12texture.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12texture.cpp
new file mode 100644
index 0000000000..18a83a75f6
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12texture.cpp
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12texture_p.h"
+#include "qsgd3d12engine_p.h"
+#include <private/qsgcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+void QSGD3D12Texture::setImage(const QImage &image, uint flags)
+{
+ // ### mipmap, atlas
+
+ const bool alphaRequest = flags & QSGRenderContext::CreateTexture_Alpha;
+ m_alphaWanted = alphaRequest && image.hasAlphaChannel();
+
+ m_size = image.size();
+
+ QSGD3D12Engine::TextureCreateFlags createFlags = 0;
+ if (m_alphaWanted)
+ createFlags |= QSGD3D12Engine::CreateWithAlpha;
+
+ m_id = m_engine->createTexture(image.format(), image.size(), createFlags);
+ if (!m_id) {
+ qWarning("Failed to allocate texture of size %dx%d", image.width(), image.height());
+ return;
+ }
+
+ QSGD3D12Engine::TextureUploadFlags uploadFlags = 0;
+ m_engine->queueTextureUpload(m_id, image, uploadFlags);
+}
+
+QSGD3D12Texture::~QSGD3D12Texture()
+{
+ if (m_id)
+ m_engine->releaseTexture(m_id);
+}
+
+int QSGD3D12Texture::textureId() const
+{
+ return m_id;
+}
+
+QSize QSGD3D12Texture::textureSize() const
+{
+ return m_size;
+}
+
+bool QSGD3D12Texture::hasAlphaChannel() const
+{
+ return m_alphaWanted;
+}
+
+bool QSGD3D12Texture::hasMipmaps() const
+{
+ return false; // ###
+}
+
+QRectF QSGD3D12Texture::normalizedTextureSubRect() const
+{
+ return QRectF(0, 0, 1, 1);
+}
+
+bool QSGD3D12Texture::isAtlasTexture() const
+{
+ return false; // ###
+}
+
+QSGTexture *QSGD3D12Texture::removedFromAtlas() const
+{
+ return nullptr; // ###
+}
+
+void QSGD3D12Texture::bind()
+{
+ // Called when the texture material updates the pipeline state. Here we
+ // know that the texture is going to be used in the current frame. Notify
+ // the engine so that it can wait for possible pending uploads in endFrame().
+ m_engine->addFrameTextureDep(m_id);
+}
+
+SIZE_T QSGD3D12Texture::srv() const
+{
+ Q_ASSERT(m_id);
+ return m_engine->textureSRV(m_id);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12texture_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12texture_p.h
new file mode 100644
index 0000000000..045d003f4a
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12texture_p.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12TEXTURE_P_H
+#define QSGD3D12TEXTURE_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 <qsgtexture.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12Engine;
+
+class QSGD3D12Texture : public QSGTexture
+{
+public:
+ QSGD3D12Texture(QSGD3D12Engine *engine) : m_engine(engine) { }
+ ~QSGD3D12Texture();
+
+ void setImage(const QImage &image, uint flags);
+
+ int textureId() const override;
+ QSize textureSize() const override;
+ bool hasAlphaChannel() const override;
+ bool hasMipmaps() const override;
+ QRectF normalizedTextureSubRect() const override;
+ bool isAtlasTexture() const override;
+ QSGTexture *removedFromAtlas() const override;
+ void bind() override;
+
+ SIZE_T srv() const;
+
+private:
+ QSGD3D12Engine *m_engine;
+ uint m_id = 0;
+ bool m_alphaWanted = false;
+ QSize m_size;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri b/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri
index 0f80405162..cc0a8cb15d 100644
--- a/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri
+++ b/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri
@@ -28,9 +28,20 @@ smoothcolor_pshader.header = ps_smoothcolor.hlslh
smoothcolor_pshader.entry = PS_SmoothColor
smoothcolor_pshader.type = ps_5_0
+texture_VSPS = $$PWD/texture.hlsl
+texture_vshader.input = texture_VSPS
+texture_vshader.header = vs_texture.hlslh
+texture_vshader.entry = VS_Texture
+texture_vshader.type = vs_5_0
+texture_pshader.input = texture_VSPS
+texture_pshader.header = ps_texture.hlslh
+texture_pshader.entry = PS_Texture
+texture_pshader.type = ps_5_0
+
HLSL_SHADERS = \
vertexcolor_vshader vertexcolor_pshader \
stencilclip_vshader stencilclip_pshader \
- smoothcolor_vshader smoothcolor_pshader
+ smoothcolor_vshader smoothcolor_pshader \
+ texture_vshader texture_pshader
load(hlsl_bytecode_header)
diff --git a/src/quick/scenegraph/adaptations/d3d12/shaders/texture.hlsl b/src/quick/scenegraph/adaptations/d3d12/shaders/texture.hlsl
new file mode 100644
index 0000000000..1ae6579e8d
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/shaders/texture.hlsl
@@ -0,0 +1,33 @@
+struct VSInput
+{
+ float4 position : POSITION;
+ float2 coord : TEXCOORD0;
+};
+
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 mvp;
+ float opacity;
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+ float2 coord : TEXCOORD0;
+};
+
+Texture2D tex : register(t0);
+SamplerState samp : register(s0);
+
+PSInput VS_Texture(VSInput input)
+{
+ PSInput result;
+ result.position = mul(mvp, input.position);
+ result.coord = input.coord;
+ return result;
+}
+
+float4 PS_Texture(PSInput input) : SV_TARGET
+{
+ return tex.Sample(samp, input.coord) * opacity;
+}
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.h b/src/quick/scenegraph/coreapi/qsggeometry.h
index 6b1e3e3f4e..5d1ed7b263 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.h
+++ b/src/quick/scenegraph/coreapi/qsggeometry.h
@@ -58,7 +58,9 @@ public:
UNKNOWN,
POSITION,
COLOR,
- TEXCOORD
+ TEXCOORD,
+ TEXCOORD1,
+ TEXCOORD2
};
int position;
diff --git a/src/quick/scenegraph/qsgdefaultimagenode.cpp b/src/quick/scenegraph/qsgdefaultimagenode.cpp
index c26092e20c..8e61bf9ec2 100644
--- a/src/quick/scenegraph/qsgdefaultimagenode.cpp
+++ b/src/quick/scenegraph/qsgdefaultimagenode.cpp
@@ -61,10 +61,10 @@ namespace
const QSGGeometry::AttributeSet &smoothAttributeSet()
{
static QSGGeometry::Attribute data[] = {
- QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),
- QSGGeometry::Attribute::create(1, 2, GL_FLOAT, false),
- QSGGeometry::Attribute::create(2, 2, GL_FLOAT, false),
- QSGGeometry::Attribute::create(3, 2, GL_FLOAT, false)
+ QSGGeometry::Attribute::createWithSemantic(0, 2, GL_FLOAT, QSGGeometry::Attribute::POSITION),
+ QSGGeometry::Attribute::createWithSemantic(1, 2, GL_FLOAT, QSGGeometry::Attribute::TEXCOORD),
+ QSGGeometry::Attribute::createWithSemantic(2, 2, GL_FLOAT, QSGGeometry::Attribute::TEXCOORD1),
+ QSGGeometry::Attribute::createWithSemantic(3, 2, GL_FLOAT, QSGGeometry::Attribute::TEXCOORD2)
};
static QSGGeometry::AttributeSet attrs = { 4, sizeof(SmoothVertex), data };
return attrs;
@@ -143,7 +143,7 @@ void SmoothTextureMaterialShader::initialize()
QSGTextureMaterialShader::initialize();
}
-QSGDefaultImageNode::QSGDefaultImageNode()
+QSGDefaultNoMaterialImageNode::QSGDefaultNoMaterialImageNode()
: m_innerSourceRect(0, 0, 1, 1)
, m_subSourceRect(0, 0, 1, 1)
, m_antialiasing(false)
@@ -151,8 +151,6 @@ QSGDefaultImageNode::QSGDefaultImageNode()
, m_dirtyGeometry(false)
, m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
{
- setMaterial(&m_materialO);
- setOpaqueMaterial(&m_material);
setGeometry(&m_geometry);
#ifdef QSG_RUNTIME_DESCRIPTION
@@ -160,7 +158,7 @@ QSGDefaultImageNode::QSGDefaultImageNode()
#endif
}
-void QSGDefaultImageNode::setTargetRect(const QRectF &rect)
+void QSGDefaultNoMaterialImageNode::setTargetRect(const QRectF &rect)
{
if (rect == m_targetRect)
return;
@@ -168,7 +166,7 @@ void QSGDefaultImageNode::setTargetRect(const QRectF &rect)
m_dirtyGeometry = true;
}
-void QSGDefaultImageNode::setInnerTargetRect(const QRectF &rect)
+void QSGDefaultNoMaterialImageNode::setInnerTargetRect(const QRectF &rect)
{
if (rect == m_innerTargetRect)
return;
@@ -176,7 +174,7 @@ void QSGDefaultImageNode::setInnerTargetRect(const QRectF &rect)
m_dirtyGeometry = true;
}
-void QSGDefaultImageNode::setInnerSourceRect(const QRectF &rect)
+void QSGDefaultNoMaterialImageNode::setInnerSourceRect(const QRectF &rect)
{
if (rect == m_innerSourceRect)
return;
@@ -184,7 +182,7 @@ void QSGDefaultImageNode::setInnerSourceRect(const QRectF &rect)
m_dirtyGeometry = true;
}
-void QSGDefaultImageNode::setSubSourceRect(const QRectF &rect)
+void QSGDefaultNoMaterialImageNode::setSubSourceRect(const QRectF &rect)
{
if (rect == m_subSourceRect)
return;
@@ -192,60 +190,12 @@ void QSGDefaultImageNode::setSubSourceRect(const QRectF &rect)
m_dirtyGeometry = true;
}
-void QSGDefaultImageNode::setFiltering(QSGTexture::Filtering filtering)
-{
- if (m_material.filtering() == filtering)
- return;
-
- m_material.setFiltering(filtering);
- m_materialO.setFiltering(filtering);
- m_smoothMaterial.setFiltering(filtering);
- markDirty(DirtyMaterial);
-}
-
-
-void QSGDefaultImageNode::setMipmapFiltering(QSGTexture::Filtering filtering)
-{
- if (m_material.mipmapFiltering() == filtering)
- return;
-
- m_material.setMipmapFiltering(filtering);
- m_materialO.setMipmapFiltering(filtering);
- m_smoothMaterial.setMipmapFiltering(filtering);
- markDirty(DirtyMaterial);
-}
-
-void QSGDefaultImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode)
-{
- if (m_material.verticalWrapMode() == wrapMode)
- return;
-
- m_material.setVerticalWrapMode(wrapMode);
- m_materialO.setVerticalWrapMode(wrapMode);
- m_smoothMaterial.setVerticalWrapMode(wrapMode);
- markDirty(DirtyMaterial);
-}
-
-void QSGDefaultImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode)
-{
- if (m_material.horizontalWrapMode() == wrapMode)
- return;
-
- m_material.setHorizontalWrapMode(wrapMode);
- m_materialO.setHorizontalWrapMode(wrapMode);
- m_smoothMaterial.setHorizontalWrapMode(wrapMode);
- markDirty(DirtyMaterial);
-}
-
-
-void QSGDefaultImageNode::setTexture(QSGTexture *texture)
+void QSGDefaultNoMaterialImageNode::setTexture(QSGTexture *texture)
{
Q_ASSERT(texture);
- m_material.setTexture(texture);
- m_materialO.setTexture(texture);
- m_smoothMaterial.setTexture(texture);
- m_material.setFlag(QSGMaterial::Blending, texture->hasAlphaChannel());
+ setMaterialTexture(texture);
+ updateMaterialBlending();
markDirty(DirtyMaterial);
@@ -253,26 +203,23 @@ void QSGDefaultImageNode::setTexture(QSGTexture *texture)
m_dirtyGeometry = true;
}
-void QSGDefaultImageNode::setAntialiasing(bool antialiasing)
+void QSGDefaultNoMaterialImageNode::setAntialiasing(bool antialiasing)
{
if (antialiasing == m_antialiasing)
return;
m_antialiasing = antialiasing;
if (m_antialiasing) {
- setMaterial(&m_smoothMaterial);
- setOpaqueMaterial(0);
setGeometry(new QSGGeometry(smoothAttributeSet(), 0));
setFlag(OwnsGeometry, true);
} else {
- setMaterial(&m_materialO);
- setOpaqueMaterial(&m_material);
setGeometry(&m_geometry);
setFlag(OwnsGeometry, false);
}
+ updateMaterialAntialiasing();
m_dirtyGeometry = true;
}
-void QSGDefaultImageNode::setMirror(bool mirror)
+void QSGDefaultNoMaterialImageNode::setMirror(bool mirror)
{
if (mirror == m_mirror)
return;
@@ -281,26 +228,24 @@ void QSGDefaultImageNode::setMirror(bool mirror)
}
-void QSGDefaultImageNode::update()
+void QSGDefaultNoMaterialImageNode::update()
{
if (m_dirtyGeometry)
updateGeometry();
}
-void QSGDefaultImageNode::preprocess()
+void QSGDefaultNoMaterialImageNode::preprocess()
{
bool doDirty = false;
- QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(m_material.texture());
+ QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(materialTexture());
if (t) {
doDirty = t->updateTexture();
if (doDirty)
updateGeometry();
}
- bool alpha = m_material.flags() & QSGMaterial::Blending;
- if (m_material.texture() && alpha != m_material.texture()->hasAlphaChannel()) {
- m_material.setFlag(QSGMaterial::Blending, !alpha);
+
+ if (updateMaterialBlending())
doDirty = true;
- }
if (doDirty)
markDirty(DirtyMaterial);
@@ -328,10 +273,10 @@ static inline void appendQuad(quint16 **indices, quint16 topLeft, quint16 topRig
*(*indices)++ = topLeft;
}
-void QSGDefaultImageNode::updateGeometry()
+void QSGDefaultNoMaterialImageNode::updateGeometry()
{
Q_ASSERT(!m_targetRect.isEmpty());
- const QSGTexture *t = m_material.texture();
+ const QSGTexture *t = materialTexture();
if (!t) {
QSGGeometry *g = geometry();
g->allocate(4);
@@ -357,25 +302,12 @@ void QSGDefaultImageNode::updateGeometry()
bool hasTiles = hTiles != 1 || vTiles != 1;
bool fullTexture = innerSourceRect == QRectF(0, 0, 1, 1);
- bool wrapSupported = true;
-
- QOpenGLContext *ctx = QOpenGLContext::currentContext();
-#ifndef QT_OPENGL_ES_2
- if (ctx->isOpenGLES())
-#endif
- {
- bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
- QSize size = t->textureSize();
- const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
- wrapSupported = npotSupported || !isNpot;
- }
-
// An image can be rendered as a single quad if:
// - There are no margins, and either:
// - the image isn't repeated
// - the source rectangle fills the entire texture so that texture wrapping can be used,
// and NPOT is supported
- if (!hasMargins && (!hasTiles || (fullTexture && wrapSupported))) {
+ if (!hasMargins && (!hasTiles || (fullTexture && supportsWrap(t->textureSize())))) {
QRectF sr;
if (!fullTexture) {
sr = QRectF(innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(),
@@ -668,4 +600,104 @@ void QSGDefaultImageNode::updateGeometry()
m_dirtyGeometry = false;
}
+QSGDefaultImageNode::QSGDefaultImageNode()
+{
+ setMaterial(&m_materialO);
+ setOpaqueMaterial(&m_material);
+}
+
+void QSGDefaultImageNode::setFiltering(QSGTexture::Filtering filtering)
+{
+ if (m_material.filtering() == filtering)
+ return;
+
+ m_material.setFiltering(filtering);
+ m_materialO.setFiltering(filtering);
+ m_smoothMaterial.setFiltering(filtering);
+ markDirty(DirtyMaterial);
+}
+
+void QSGDefaultImageNode::setMipmapFiltering(QSGTexture::Filtering filtering)
+{
+ if (m_material.mipmapFiltering() == filtering)
+ return;
+
+ m_material.setMipmapFiltering(filtering);
+ m_materialO.setMipmapFiltering(filtering);
+ m_smoothMaterial.setMipmapFiltering(filtering);
+ markDirty(DirtyMaterial);
+}
+
+void QSGDefaultImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode)
+{
+ if (m_material.verticalWrapMode() == wrapMode)
+ return;
+
+ m_material.setVerticalWrapMode(wrapMode);
+ m_materialO.setVerticalWrapMode(wrapMode);
+ m_smoothMaterial.setVerticalWrapMode(wrapMode);
+ markDirty(DirtyMaterial);
+}
+
+void QSGDefaultImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode)
+{
+ if (m_material.horizontalWrapMode() == wrapMode)
+ return;
+
+ m_material.setHorizontalWrapMode(wrapMode);
+ m_materialO.setHorizontalWrapMode(wrapMode);
+ m_smoothMaterial.setHorizontalWrapMode(wrapMode);
+ markDirty(DirtyMaterial);
+}
+
+void QSGDefaultImageNode::updateMaterialAntialiasing()
+{
+ if (m_antialiasing) {
+ setMaterial(&m_smoothMaterial);
+ setOpaqueMaterial(0);
+ } else {
+ setMaterial(&m_materialO);
+ setOpaqueMaterial(&m_material);
+ }
+}
+
+void QSGDefaultImageNode::setMaterialTexture(QSGTexture *texture)
+{
+ m_material.setTexture(texture);
+ m_materialO.setTexture(texture);
+ m_smoothMaterial.setTexture(texture);
+}
+
+QSGTexture *QSGDefaultImageNode::materialTexture() const
+{
+ return m_material.texture();
+}
+
+bool QSGDefaultImageNode::updateMaterialBlending()
+{
+ const bool alpha = m_material.flags() & QSGMaterial::Blending;
+ if (materialTexture() && alpha != materialTexture()->hasAlphaChannel()) {
+ m_material.setFlag(QSGMaterial::Blending, !alpha);
+ return true;
+ }
+ return false;
+}
+
+bool QSGDefaultImageNode::supportsWrap(const QSize &size) const
+{
+ bool wrapSupported = true;
+
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+#ifndef QT_OPENGL_ES_2
+ if (ctx->isOpenGLES())
+#endif
+ {
+ bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
+ const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
+ wrapSupported = npotSupported || !isNpot;
+ }
+
+ return wrapSupported;
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultimagenode_p.h b/src/quick/scenegraph/qsgdefaultimagenode_p.h
index 2d8abc1d35..85de09b6eb 100644
--- a/src/quick/scenegraph/qsgdefaultimagenode_p.h
+++ b/src/quick/scenegraph/qsgdefaultimagenode_p.h
@@ -65,31 +65,31 @@ public:
void setTexture(QSGTexture *texture);
protected:
- virtual QSGMaterialType *type() const;
- virtual QSGMaterialShader *createShader() const;
+ QSGMaterialType *type() const override;
+ QSGMaterialShader *createShader() const override;
};
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultImageNode : public QSGImageNode
+class Q_QUICK_PRIVATE_EXPORT QSGDefaultNoMaterialImageNode : public QSGImageNode
{
public:
- QSGDefaultImageNode();
- virtual void setTargetRect(const QRectF &rect);
- virtual void setInnerTargetRect(const QRectF &rect);
- virtual void setInnerSourceRect(const QRectF &rect);
- virtual void setSubSourceRect(const QRectF &rect);
- virtual void setTexture(QSGTexture *t);
- virtual void setAntialiasing(bool antialiasing);
- virtual void setMirror(bool mirror);
- virtual void update();
-
- virtual void setMipmapFiltering(QSGTexture::Filtering filtering);
- virtual void setFiltering(QSGTexture::Filtering filtering);
- virtual void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode);
- virtual void setVerticalWrapMode(QSGTexture::WrapMode wrapMode);
-
- virtual void preprocess();
+ QSGDefaultNoMaterialImageNode();
+ void setTargetRect(const QRectF &rect) override;
+ void setInnerTargetRect(const QRectF &rect) override;
+ void setInnerSourceRect(const QRectF &rect) override;
+ void setSubSourceRect(const QRectF &rect) override;
+ void setTexture(QSGTexture *texture) override;
+ void setAntialiasing(bool antialiasing) override;
+ void setMirror(bool mirror) override;
+ void update() override;
+ void preprocess() override;
+
+protected:
+ virtual void updateMaterialAntialiasing() = 0;
+ virtual void setMaterialTexture(QSGTexture *texture) = 0;
+ virtual QSGTexture *materialTexture() const = 0;
+ virtual bool updateMaterialBlending() = 0;
+ virtual bool supportsWrap(const QSize &size) const = 0;
-private:
void updateGeometry();
QRectF m_targetRect;
@@ -97,10 +97,6 @@ private:
QRectF m_innerSourceRect;
QRectF m_subSourceRect;
- QSGOpaqueTextureMaterial m_material;
- QSGTextureMaterial m_materialO;
- QSGSmoothTextureMaterial m_smoothMaterial;
-
uint m_antialiasing : 1;
uint m_mirror : 1;
uint m_dirtyGeometry : 1;
@@ -108,6 +104,28 @@ private:
QSGGeometry m_geometry;
};
+class Q_QUICK_PRIVATE_EXPORT QSGDefaultImageNode : public QSGDefaultNoMaterialImageNode
+{
+public:
+ QSGDefaultImageNode();
+
+ void setMipmapFiltering(QSGTexture::Filtering filtering) override;
+ void setFiltering(QSGTexture::Filtering filtering) override;
+ void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) override;
+ void setVerticalWrapMode(QSGTexture::WrapMode wrapMode) override;
+
+ void updateMaterialAntialiasing() override;
+ void setMaterialTexture(QSGTexture *texture) override;
+ QSGTexture *materialTexture() const override;
+ bool updateMaterialBlending() override;
+ bool supportsWrap(const QSize &size) const override;
+
+private:
+ QSGOpaqueTextureMaterial m_material;
+ QSGTextureMaterial m_materialO;
+ QSGSmoothTextureMaterial m_smoothMaterial;
+};
+
QT_END_NAMESPACE
#endif
diff --git a/tests/manual/nodetypes/Images.qml b/tests/manual/nodetypes/Images.qml
new file mode 100644
index 0000000000..34badcdb4b
--- /dev/null
+++ b/tests/manual/nodetypes/Images.qml
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ Rectangle {
+ width: 100
+ height: 100
+ anchors.centerIn: parent
+ color: "red"
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ }
+
+ Image {
+ source: "qrc:/qt.png"
+
+ Image {
+ anchors.centerIn: parent
+ source: "qrc:/face-smile.png"
+ }
+ }
+}
diff --git a/tests/manual/nodetypes/main.qml b/tests/manual/nodetypes/main.qml
index 3639b8c5b2..94f154afdd 100644
--- a/tests/manual/nodetypes/main.qml
+++ b/tests/manual/nodetypes/main.qml
@@ -56,5 +56,8 @@ Item {
loader.source = "qrc:/Rects.qml";
if (event.key === Qt.Key_4)
loader.source = "qrc:/LotsOfRects.qml";
+
+ if (event.key === Qt.Key_I)
+ loader.source = "qrc:/Images.qml";
}
}
diff --git a/tests/manual/nodetypes/nodetypes.cpp b/tests/manual/nodetypes/nodetypes.cpp
index bd9d1937d8..6e4b6db2a2 100644
--- a/tests/manual/nodetypes/nodetypes.cpp
+++ b/tests/manual/nodetypes/nodetypes.cpp
@@ -50,6 +50,7 @@ int main(int argc, char **argv)
qDebug("Available tests:");
qDebug(" [R] - Rectangles");
qDebug(" [4] - A lot of rectangles (perf)");
+ qDebug(" [I] - Images");
qDebug("\nPress S to stop the currently running test\n");
QQuickView view;
diff --git a/tests/manual/nodetypes/nodetypes.pro b/tests/manual/nodetypes/nodetypes.pro
index 4ac026fc41..ae5a60e1bd 100644
--- a/tests/manual/nodetypes/nodetypes.pro
+++ b/tests/manual/nodetypes/nodetypes.pro
@@ -4,4 +4,4 @@ SOURCES += nodetypes.cpp
RESOURCES += nodetypes.qrc
-OTHER_FILES += main.qml Rects.qml
+OTHER_FILES += main.qml Rects.qml LotsOfRects.qml Images.qml
diff --git a/tests/manual/nodetypes/nodetypes.qrc b/tests/manual/nodetypes/nodetypes.qrc
index d942ee2396..924a262eb9 100644
--- a/tests/manual/nodetypes/nodetypes.qrc
+++ b/tests/manual/nodetypes/nodetypes.qrc
@@ -3,5 +3,8 @@
<file>main.qml</file>
<file>Rects.qml</file>
<file>LotsOfRects.qml</file>
+ <file>Images.qml</file>
+ <file>qt.png</file>
+ <file>face-smile.png</file>
</qresource>
</RCC>
diff --git a/tests/manual/nodetypes/qt.png b/tests/manual/nodetypes/qt.png
new file mode 100644
index 0000000000..f30eec0d4d
--- /dev/null
+++ b/tests/manual/nodetypes/qt.png
Binary files differ