aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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