aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp5
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp593
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h31
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h106
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12layer.cpp186
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12layer_p.h19
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp75
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer_p.h11
-rw-r--r--src/quick/scenegraph/qsgbasicimagenode.cpp15
-rw-r--r--src/quick/scenegraph/qsgbasicimagenode_p.h4
-rw-r--r--tests/manual/nodetypes/Layers.qml106
-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.qrc1
15 files changed, 835 insertions, 323 deletions
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp
index 7c1807466c..0bb342226b 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp
@@ -42,6 +42,7 @@
#include "qsgd3d12rectanglenode_p.h"
#include "qsgd3d12imagenode_p.h"
#include "qsgd3d12glyphnode_p.h"
+#include "qsgd3d12layer_p.h"
QT_BEGIN_NAMESPACE
@@ -83,9 +84,7 @@ QSGNinePatchNode *QSGD3D12Context::createNinePatchNode()
QSGLayer *QSGD3D12Context::createLayer(QSGRenderContext *rc)
{
- Q_UNUSED(rc);
- Q_UNREACHABLE();
- return nullptr;
+ return new QSGD3D12Layer(static_cast<QSGD3D12RenderContext *>(rc));
}
QSize QSGD3D12Context::minimumFBOSize() const
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp
index 9bfd25cf5c..230f6d8984 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp
@@ -350,39 +350,39 @@ void QSGD3D12Engine::endFrame()
d->endFrame();
}
-void QSGD3D12Engine::finalizePipeline(const QSGD3D12PipelineState &pipelineState)
+void QSGD3D12Engine::beginLayer()
{
- d->finalizePipeline(pipelineState);
+ d->beginLayer();
}
-void QSGD3D12Engine::resetVertexBuffer(const quint8 *data, int size)
+void QSGD3D12Engine::endLayer()
{
- d->resetVertexBuffer(data, size);
+ d->endLayer();
}
-void QSGD3D12Engine::markVertexBufferDirty(int offset, int size)
+void QSGD3D12Engine::finalizePipeline(const QSGD3D12PipelineState &pipelineState)
{
- d->markVertexBufferDirty(offset, size);
+ d->finalizePipeline(pipelineState);
}
-void QSGD3D12Engine::resetIndexBuffer(const quint8 *data, int size)
+uint QSGD3D12Engine::genBuffer()
{
- d->resetIndexBuffer(data, size);
+ return d->genBuffer();
}
-void QSGD3D12Engine::markIndexBufferDirty(int offset, int size)
+void QSGD3D12Engine::releaseBuffer(uint id)
{
- d->markIndexBufferDirty(offset, size);
+ d->releaseBuffer(id);
}
-void QSGD3D12Engine::resetConstantBuffer(const quint8 *data, int size)
+void QSGD3D12Engine::resetBuffer(uint id, const quint8 *data, int size)
{
- d->resetConstantBuffer(data, size);
+ d->resetBuffer(id, data, size);
}
-void QSGD3D12Engine::markConstantBufferDirty(int offset, int size)
+void QSGD3D12Engine::markBufferDirty(uint id, int offset, int size)
{
- d->markConstantBufferDirty(offset, size);
+ d->markBufferDirty(id, offset, size);
}
void QSGD3D12Engine::queueViewport(const QRect &rect)
@@ -420,11 +420,9 @@ void QSGD3D12Engine::queueSetStencilRef(quint32 ref)
d->queueSetStencilRef(ref);
}
-void QSGD3D12Engine::queueDraw(QSGGeometry::DrawingMode mode, int count, int vboOffset, int vboSize, int vboStride,
- int cboOffset,
- int startIndexIndex, QSGD3D12Format indexFormat)
+void QSGD3D12Engine::queueDraw(const DrawParams &params)
{
- d->queueDraw(mode, count, vboOffset, vboSize, vboStride, cboOffset, startIndexIndex, indexFormat);
+ d->queueDraw(params);
}
void QSGD3D12Engine::present()
@@ -594,12 +592,15 @@ void QSGD3D12EnginePrivate::releaseResources()
mipmapper.releaseResources();
- commandList = nullptr;
+ frameCommandList = nullptr;
copyCommandList = nullptr;
copyCommandAllocator = nullptr;
- for (int i = 0; i < frameInFlightCount; ++i)
- commandAllocator[i] = nullptr;
+ for (int i = 0; i < frameInFlightCount; ++i) {
+ frameCommandAllocator[i] = nullptr;
+ pframeData[i].gpuCbvSrvUavHeap = nullptr;
+ delete frameFence[i];
+ }
defaultDS = nullptr;
for (int i = 0; i < swapChainBufferCount; ++i) {
@@ -607,15 +608,9 @@ void QSGD3D12EnginePrivate::releaseResources()
defaultRT[i] = nullptr;
}
- for (int i = 0; i < frameInFlightCount; ++i) {
- pframeData[i].vertex.buffer = nullptr;
- pframeData[i].index.buffer = nullptr;
- pframeData[i].constant.buffer = nullptr;
- pframeData[i].gpuCbvSrvUavHeap = nullptr;
- }
-
psoCache.clear();
rootSigCache.clear();
+ buffers.clear();
textures.clear();
renderTargets.clear();
@@ -626,8 +621,6 @@ void QSGD3D12EnginePrivate::releaseResources()
swapChain = nullptr;
delete presentFence;
- for (int i = 0; i < frameInFlightCount; ++i)
- delete frameFence[i];
textureUploadFence = nullptr;
deviceManager()->unref();
@@ -725,7 +718,7 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int
}
for (int i = 0; i < frameInFlightCount; ++i) {
- if (FAILED(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAllocator[i])))) {
+ if (FAILED(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&frameCommandAllocator[i])))) {
qWarning("Failed to create command allocator");
return;
}
@@ -745,13 +738,13 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int
setupDefaultRenderTargets();
- if (FAILED(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocator[0].Get(),
- nullptr, IID_PPV_ARGS(&commandList)))) {
+ if (FAILED(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, frameCommandAllocator[0].Get(),
+ nullptr, IID_PPV_ARGS(&frameCommandList)))) {
qWarning("Failed to create command list");
return;
}
// created in recording state, close it for now
- commandList->Close();
+ frameCommandList->Close();
if (FAILED(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_COPY, copyCommandAllocator.Get(),
nullptr, IID_PPV_ARGS(&copyCommandList)))) {
@@ -771,10 +764,6 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int
return;
}
- vertexData = CPUBufferRef();
- indexData = CPUBufferRef();
- constantData = CPUBufferRef();
-
psoCache.setMaxCost(MAX_CACHED_PSO);
rootSigCache.setMaxCost(MAX_CACHED_ROOTSIG);
@@ -1083,82 +1072,65 @@ ID3D12Resource *QSGD3D12EnginePrivate::createBuffer(int size)
return buf;
}
-void QSGD3D12EnginePrivate::markCPUBufferDirty(CPUBufferRef *dst, PersistentFrameData::ChangeTrackedBuffer *buf, int offset, int size)
+void QSGD3D12EnginePrivate::ensureBuffer(Buffer *buf)
{
- if (!inFrame) {
- qWarning("%s: Cannot be called outside begin/endFrame", __FUNCTION__);
- return;
- }
-
- // Bail out when there was a resetV/I/CBuffer and the dirty list already spans the entire buffer data.
- if (!dst->dirty.isEmpty()) {
- if (dst->dirty[0].first == 0 && dst->dirty[0].second == dst->size)
- return;
- }
-
- const QPair<int, int> range = qMakePair(offset, size);
- if (!dst->dirty.contains(range))
- dst->dirty.append(range);
- if (!buf->totalDirtyInFrame.contains(range))
- buf->totalDirtyInFrame.append(range);
-}
-
-void QSGD3D12EnginePrivate::ensureBuffer(CPUBufferRef *src, PersistentFrameData::ChangeTrackedBuffer *buf, const char *dbgstr)
-{
- if (src->allDirty) {
- src->allDirty = false;
- // Only enlarge, never shrink
- const bool newBufferNeeded = buf->buffer ? (src->size > buf->buffer->GetDesc().Width) : true;
- if (newBufferNeeded) {
- // Round it up and overallocate a little bit so that a subsequent
- // buffer contents rebuild with a slightly larger total size does
- // not lead to creating a new buffer.
- quint32 sz = alignedSize(src->size, 4096);
- if (Q_UNLIKELY(debug_render()))
- qDebug("new %s buffer of size %d (actual data size %d)", dbgstr, sz, src->size);
- buf->buffer.Attach(createBuffer(sz));
- }
- // Cache the actual data size in the per-in-flight-frame data as well.
- buf->dataSize = src->size;
- // Mark everything as dirty.
- src->dirty.clear();
- buf->totalDirtyInFrame.clear();
- if (buf->buffer) {
- const QPair<int, int> range = qMakePair(0, src->size);
- src->dirty.append(range);
- buf->totalDirtyInFrame.append(range);
- }
+ Buffer::InFlightData &bfd(buf->d[currentPFrameIndex]);
+ // Only enlarge, never shrink
+ const bool newBufferNeeded = bfd.buffer ? (buf->cpuDataRef.size > bfd.resourceSize) : true;
+ if (newBufferNeeded) {
+ // Round it up and overallocate a little bit so that a subsequent
+ // buffer contents rebuild with a slightly larger total size does
+ // not lead to creating a new buffer.
+ const quint32 sz = alignedSize(buf->cpuDataRef.size, 4096);
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("new buffer[pf=%d] of size %d (actual data size %d)", currentPFrameIndex, sz, buf->cpuDataRef.size);
+ bfd.buffer.Attach(createBuffer(sz));
+ bfd.resourceSize = sz;
}
+ // Cache the actual data size in the per-in-flight-frame data as well.
+ bfd.dataSize = buf->cpuDataRef.size;
}
-void QSGD3D12EnginePrivate::updateBuffer(CPUBufferRef *src, PersistentFrameData::ChangeTrackedBuffer *buf, const char *dbgstr)
+void QSGD3D12EnginePrivate::updateBuffer(Buffer *buf)
{
+ if (buf->cpuDataRef.dirty.isEmpty())
+ return;
+
+ Buffer::InFlightData &bfd(buf->d[currentPFrameIndex]);
quint8 *p = nullptr;
const D3D12_RANGE readRange = { 0, 0 };
- if (!src->dirty.isEmpty()) {
- if (FAILED(buf->buffer->Map(0, &readRange, reinterpret_cast<void **>(&p)))) {
- qWarning("Map failed for %s buffer of size %d", dbgstr, src->size);
- return;
- }
- for (const auto &r : qAsConst(src->dirty)) {
- if (Q_UNLIKELY(debug_render()))
- qDebug("%s o %d s %d", dbgstr, r.first, r.second);
- memcpy(p + r.first, src->p + r.first, r.second);
- }
- buf->buffer->Unmap(0, nullptr);
- src->dirty.clear();
+ if (FAILED(bfd.buffer->Map(0, &readRange, reinterpret_cast<void **>(&p)))) {
+ qWarning("Map failed for buffer of size %d", buf->cpuDataRef.size);
+ return;
+ }
+ for (const auto &r : qAsConst(buf->cpuDataRef.dirty)) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("%p o %d s %d", buf, r.first, r.second);
+ memcpy(p + r.first, buf->cpuDataRef.p + r.first, r.second);
}
+ bfd.buffer->Unmap(0, nullptr);
+ buf->cpuDataRef.dirty.clear();
}
void QSGD3D12EnginePrivate::beginFrame()
{
- if (inFrame)
- qWarning("beginFrame called again without an endFrame");
-
- inFrame = true;
+ if (inFrame && !activeLayers)
+ qFatal("beginFrame called again without an endFrame, frame index was %d", frameIndex);
if (Q_UNLIKELY(debug_render()))
- qDebug() << "***** begin frame, logical" << frameIndex << "present" << presentFrameIndex;
+ qDebug() << "***** begin frame, logical" << frameIndex << "present" << presentFrameIndex << "layer" << activeLayers;
+
+ if (inFrame && activeLayers) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("frame %d already in progress", frameIndex);
+ if (!currentLayerDepth) {
+ // There are layers and the real frame preparation starts now. Prepare for present.
+ beginFrameDraw();
+ }
+ return;
+ }
+
+ inFrame = true;
// The device may have been lost. This is the point to attempt to start again from scratch.
if (!initialized && window)
@@ -1179,34 +1151,44 @@ void QSGD3D12EnginePrivate::beginFrame()
fence->SetEventOnCompletion(inFlightFenceValue, event);
WaitForSingleObject(event, INFINITE);
}
- commandAllocator[currentPFrameIndex]->Reset();
+ frameCommandAllocator[currentPFrameIndex]->Reset();
}
PersistentFrameData &pfd(pframeData[currentPFrameIndex]);
pfd.cbvSrvUavNextFreeDescriptorIndex = 0;
- pfd.vertex.totalDirtyInFrame.clear();
- pfd.index.totalDirtyInFrame.clear();
- pfd.constant.totalDirtyInFrame.clear();
-
- Q_ASSERT(vertexData.dirty.isEmpty() && indexData.dirty.isEmpty() && constantData.dirty.isEmpty());
+ for (Buffer &b : buffers) {
+ if (b.entryInUse())
+ b.d[currentPFrameIndex].dirty.clear();
+ }
if (frameIndex >= frameInFlightCount) {
- // Now sync the buffer changes from the previous, potentially still in flight, frames.
- for (int delta = 1; delta < frameInFlightCount; ++delta) {
- PersistentFrameData &prevFrameData(pframeData[(frameIndex - delta) % frameInFlightCount]);
- if (pfd.vertex.buffer && pfd.vertex.dataSize == vertexData.size)
- vertexData.dirty.append(prevFrameData.vertex.totalDirtyInFrame);
- else
- vertexData.allDirty = true;
- if (pfd.index.buffer && pfd.index.dataSize == indexData.size)
- indexData.dirty.append(prevFrameData.index.totalDirtyInFrame);
- else
- indexData.allDirty = true;
- if (pfd.constant.buffer && pfd.constant.dataSize == constantData.size)
- constantData.dirty.append(prevFrameData.constant.totalDirtyInFrame);
- else
- constantData.allDirty = true;
+ // Now sync the buffer changes from the previous, potentially still in
+ // flight, frames. This is done by taking the ranges dirtied in those
+ // frames and adding them to the global CPU-side buffer's dirty list,
+ // as if this frame changed those ranges. (however, dirty ranges
+ // inherited this way are not added to this frame's persistent
+ // per-frame dirty list because the next frame after this one should
+ // inherit this frame's genuine changes only, the rest will come from
+ // the earlier ones)
+ for (int delta = frameInFlightCount - 1; delta >= 1; --delta) {
+ const int prevPFrameIndex = (frameIndex - delta) % frameInFlightCount;
+ PersistentFrameData &prevFrameData(pframeData[prevPFrameIndex]);
+ for (uint id : qAsConst(prevFrameData.buffersUsedInFrame)) {
+ Buffer &b(buffers[id - 1]);
+ if (b.d[currentPFrameIndex].buffer && b.d[currentPFrameIndex].dataSize == b.cpuDataRef.size) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug() << "frame" << frameIndex << "takes dirty" << b.d[prevPFrameIndex].dirty
+ << "from frame" << frameIndex - delta << "for buffer" << id;
+ for (const auto &range : qAsConst(b.d[prevPFrameIndex].dirty))
+ addDirtyRange(&b.cpuDataRef.dirty, range.first, range.second, b.cpuDataRef.size);
+ } else {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug() << "frame" << frameIndex << "makes all dirty from frame" << frameIndex - delta
+ << "for buffer" << id;
+ addDirtyRange(&b.cpuDataRef.dirty, 0, b.cpuDataRef.size, b.cpuDataRef.size);
+ }
+ }
}
// Do some texture upload bookkeeping.
@@ -1274,42 +1256,67 @@ void QSGD3D12EnginePrivate::beginFrame()
pfd.outOfFrameDeleteQueue.clear();
}
- // Mark released texture slots free.
- if (!pfd.pendingTextureReleases.isEmpty()) {
- for (uint id : qAsConst(pfd.pendingTextureReleases)) {
- Texture &t(textures[id - 1]);
- t.flags &= ~RenderTarget::EntryInUse; // createTexture() can now reuse this entry
- t.texture = nullptr;
+ // Mark released texture, buffer, etc. slots free.
+ if (!pfd.pendingReleases.isEmpty()) {
+ for (const auto &pr : qAsConst(pfd.pendingReleases)) {
+ Q_ASSERT(pr.id);
+ if (pr.type == PersistentFrameData::PendingRelease::TypeTexture) {
+ Texture &t(textures[pr.id - 1]);
+ Q_ASSERT(t.entryInUse());
+ t.flags &= ~RenderTarget::EntryInUse; // createTexture() can now reuse this entry
+ t.texture = nullptr;
+ } else if (pr.type == PersistentFrameData::PendingRelease::TypeBuffer) {
+ Buffer &b(buffers[pr.id - 1]);
+ Q_ASSERT(b.entryInUse());
+ b.flags &= ~Buffer::EntryInUse;
+ for (int i = 0; i < frameInFlightCount; ++i)
+ b.d[i].buffer = nullptr;
+ } else {
+ qFatal("Corrupt pending release list, type %d", pr.type);
+ }
}
- pfd.pendingTextureReleases.clear();
+ pfd.pendingReleases.clear();
}
- if (!pfd.outOfFramePendingTextureReleases.isEmpty()) {
- pfd.pendingTextureReleases = pfd.outOfFramePendingTextureReleases;
- pfd.outOfFramePendingTextureReleases.clear();
+ if (!pfd.outOfFramePendingReleases.isEmpty()) {
+ pfd.pendingReleases = pfd.outOfFramePendingReleases;
+ pfd.outOfFramePendingReleases.clear();
}
}
- beginDrawCalls(true);
+ pfd.buffersUsedInFrame.clear();
+
+ beginDrawCalls();
+
+ // Prepare for present if this is a frame without layers.
+ if (!activeLayers)
+ beginFrameDraw();
}
-void QSGD3D12EnginePrivate::beginDrawCalls(bool firstInFrame)
+void QSGD3D12EnginePrivate::beginDrawCalls()
{
- commandList->Reset(commandAllocator[frameIndex % frameInFlightCount].Get(), nullptr);
+ frameCommandList->Reset(frameCommandAllocator[frameIndex % frameInFlightCount].Get(), nullptr);
+ commandList = frameCommandList.Get();
tframeData.drawingMode = QSGGeometry::DrawingMode(-1);
- tframeData.indexBufferSet = false;
+ tframeData.currentIndexBuffer = 0;
tframeData.drawCount = 0;
tframeData.lastPso = nullptr;
tframeData.lastRootSig = nullptr;
tframeData.descHeapSet = false;
+}
- if (firstInFrame && windowSamples == 1)
- transitionResource(defaultRT[presentFrameIndex % swapChainBufferCount].Get(), commandList.Get(),
+void QSGD3D12EnginePrivate::beginFrameDraw()
+{
+ if (windowSamples == 1)
+ transitionResource(defaultRT[presentFrameIndex % swapChainBufferCount].Get(), commandList,
D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
}
void QSGD3D12EnginePrivate::endFrame()
{
+ if (!inFrame)
+ qFatal("endFrame called without beginFrame, frame index %d", frameIndex);
+
if (Q_UNLIKELY(debug_render()))
qDebug("***** end frame");
@@ -1326,9 +1333,13 @@ void QSGD3D12EnginePrivate::endDrawCalls(bool lastInFrame)
PersistentFrameData &pfd(pframeData[currentPFrameIndex]);
// Now is the time to sync all the changed areas in the buffers.
- updateBuffer(&vertexData, &pfd.vertex, "vertex");
- updateBuffer(&indexData, &pfd.index, "index");
- updateBuffer(&constantData, &pfd.constant, "constant");
+ if (Q_UNLIKELY(debug_render()))
+ qDebug() << "buffers used in drawcall set" << pfd.buffersUsedInDrawCallSet;
+ for (uint id : qAsConst(pfd.buffersUsedInDrawCallSet))
+ updateBuffer(&buffers[id - 1]);
+
+ pfd.buffersUsedInFrame += pfd.buffersUsedInDrawCallSet;
+ pfd.buffersUsedInDrawCallSet.clear();
// Add a wait on the 3D queue for the relevant texture uploads on the copy queue.
if (!pfd.pendingTextureUploads.isEmpty()) {
@@ -1360,11 +1371,11 @@ void QSGD3D12EnginePrivate::endDrawCalls(bool lastInFrame)
}
}
- // Resolve and transition the backbuffer for present, if needed.
if (lastInFrame) {
+ // Resolve and transition the backbuffer for present, if needed.
const int idx = presentFrameIndex % swapChainBufferCount;
if (windowSamples == 1) {
- transitionResource(defaultRT[idx].Get(), commandList.Get(),
+ transitionResource(defaultRT[idx].Get(), commandList,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
} else {
if (Q_UNLIKELY(debug_render())) {
@@ -1373,20 +1384,60 @@ void QSGD3D12EnginePrivate::endDrawCalls(bool lastInFrame)
desc.SampleDesc.Count, desc.SampleDesc.Quality);
}
resolveMultisampledTarget(defaultRT[idx].Get(), backBufferRT[idx].Get(),
- D3D12_RESOURCE_STATE_PRESENT, commandList.Get());
+ D3D12_RESOURCE_STATE_PRESENT, commandList);
+ }
+
+ if (activeLayers) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("this frame had %d layers", activeLayers);
+ activeLayers = 0;
}
}
// Go!
- HRESULT hr = commandList->Close();
+ HRESULT hr = frameCommandList->Close();
if (FAILED(hr)) {
qWarning("Failed to close command list: 0x%x", hr);
if (hr == E_INVALIDARG)
qWarning("Invalid arguments. Some of the commands in the list is invalid in some way.");
}
- ID3D12CommandList *commandLists[] = { commandList.Get() };
+ ID3D12CommandList *commandLists[] = { frameCommandList.Get() };
commandQueue->ExecuteCommandLists(_countof(commandLists), commandLists);
+
+ commandList = nullptr;
+}
+
+void QSGD3D12EnginePrivate::beginLayer()
+{
+ if (inFrame && !activeLayers)
+ qFatal("Layer rendering cannot be started while a frame is active");
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("===== beginLayer active %d depth %d (inFrame=%d)", activeLayers, currentLayerDepth, inFrame);
+
+ ++activeLayers;
+ ++currentLayerDepth;
+
+ // Do an early beginFrame. With multiple layers this results in
+ // beginLayer - beginFrame - endLayer - beginLayer - beginFrame - endLayer - ... - (*) beginFrame - endFrame
+ // where (*) denotes the start of the preparation of the actual, non-layer frame.
+
+ if (activeLayers == 1)
+ beginFrame();
+}
+
+void QSGD3D12EnginePrivate::endLayer()
+{
+ if (!inFrame || !activeLayers || !currentLayerDepth)
+ qFatal("Mismatched endLayer");
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("===== endLayer active %d depth %d", activeLayers, currentLayerDepth);
+
+ --currentLayerDepth;
+
+ // Do not touch activeLayers. It remains valid until endFrame.
}
// Root signature:
@@ -1604,42 +1655,6 @@ void QSGD3D12EnginePrivate::setDescriptorHeaps(bool force)
}
}
-void QSGD3D12EnginePrivate::resetVertexBuffer(const quint8 *data, int size)
-{
- vertexData.p = data;
- vertexData.size = size;
- vertexData.allDirty = true;
-}
-
-void QSGD3D12EnginePrivate::markVertexBufferDirty(int offset, int size)
-{
- markCPUBufferDirty(&vertexData, &pframeData[currentPFrameIndex].vertex, offset, size);
-}
-
-void QSGD3D12EnginePrivate::resetIndexBuffer(const quint8 *data, int size)
-{
- indexData.p = data;
- indexData.size = size;
- indexData.allDirty = true;
-}
-
-void QSGD3D12EnginePrivate::markIndexBufferDirty(int offset, int size)
-{
- markCPUBufferDirty(&indexData, &pframeData[currentPFrameIndex].index, offset, size);
-}
-
-void QSGD3D12EnginePrivate::resetConstantBuffer(const quint8 *data, int size)
-{
- constantData.p = data;
- constantData.size = size;
- constantData.allDirty = true;
-}
-
-void QSGD3D12EnginePrivate::markConstantBufferDirty(int offset, int size)
-{
- markCPUBufferDirty(&constantData, &pframeData[currentPFrameIndex].constant, offset, size);
-}
-
void QSGD3D12EnginePrivate::queueViewport(const QRect &rect)
{
if (!inFrame) {
@@ -1679,7 +1694,7 @@ void QSGD3D12EnginePrivate::queueSetRenderTarget(uint id)
dsvHandle = defaultDSV;
} else {
const int idx = id - 1;
- Q_ASSERT(idx < renderTargets.count());
+ Q_ASSERT(idx < renderTargets.count() && renderTargets[idx].entryInUse());
RenderTarget &rt(renderTargets[idx]);
rtvHandle = rt.rtv;
dsvHandle = rt.dsv;
@@ -1699,7 +1714,10 @@ void QSGD3D12EnginePrivate::queueClearRenderTarget(const QColor &color)
}
const float clearColor[] = { float(color.redF()), float(color.blueF()), float(color.greenF()), float(color.alphaF()) };
- commandList->ClearRenderTargetView(defaultRTV[presentFrameIndex % swapChainBufferCount], clearColor, 0, nullptr);
+ D3D12_CPU_DESCRIPTOR_HANDLE rtv = !currentRenderTarget
+ ? defaultRTV[presentFrameIndex % swapChainBufferCount]
+ : renderTargets[currentRenderTarget - 1].rtv;
+ commandList->ClearRenderTargetView(rtv, clearColor, 0, nullptr);
}
void QSGD3D12EnginePrivate::queueClearDepthStencil(float depthValue, quint8 stencilValue, QSGD3D12Engine::ClearFlags which)
@@ -1709,7 +1727,10 @@ void QSGD3D12EnginePrivate::queueClearDepthStencil(float depthValue, quint8 sten
return;
}
- commandList->ClearDepthStencilView(defaultDSV, D3D12_CLEAR_FLAGS(int(which)), depthValue, stencilValue, 0, nullptr);
+ D3D12_CPU_DESCRIPTOR_HANDLE dsv = !currentRenderTarget
+ ? defaultDSV
+ : renderTargets[currentRenderTarget - 1].dsv;
+ commandList->ClearDepthStencilView(dsv, D3D12_CLEAR_FLAGS(int(which)), depthValue, stencilValue, 0, nullptr);
}
void QSGD3D12EnginePrivate::queueSetBlendFactor(const QVector4D &factor)
@@ -1735,9 +1756,7 @@ void QSGD3D12EnginePrivate::queueSetStencilRef(quint32 ref)
commandList->OMSetStencilRef(ref);
}
-void QSGD3D12EnginePrivate::queueDraw(QSGGeometry::DrawingMode mode, int count, int vboOffset, int vboSize, int vboStride,
- int cboOffset,
- int startIndexIndex, QSGD3D12Format indexFormat)
+void QSGD3D12EnginePrivate::queueDraw(const QSGD3D12Engine::DrawParams &params)
{
if (!inFrame) {
qWarning("%s: Cannot be called outside begin/endFrame", __FUNCTION__);
@@ -1746,35 +1765,40 @@ void QSGD3D12EnginePrivate::queueDraw(QSGGeometry::DrawingMode mode, int count,
PersistentFrameData &pfd(pframeData[currentPFrameIndex]);
- // Ensure buffers are created but do not copy the data here, leave that to endDrawCalls().
- ensureBuffer(&vertexData, &pfd.vertex, "vertex");
- if (!pfd.vertex.buffer)
- return;
-
- if (indexData.size > 0) {
- ensureBuffer(&indexData, &pfd.index, "index");
- if (!pfd.index.buffer)
- return;
- } else if (indexData.allDirty) {
- indexData.allDirty = false;
- pfd.index.buffer = nullptr;
+ pfd.buffersUsedInDrawCallSet.insert(params.vertexBuf);
+ const int vertexBufIdx = params.vertexBuf - 1;
+ Q_ASSERT(params.vertexBuf && vertexBufIdx < buffers.count() && buffers[vertexBufIdx].entryInUse());
+ pfd.buffersUsedInDrawCallSet.insert(params.constantBuf);
+ const int constantBufIdx = params.constantBuf - 1;
+ Q_ASSERT(params.constantBuf && constantBufIdx < buffers.count() && buffers[constantBufIdx].entryInUse());
+ int indexBufIdx = -1;
+ if (params.indexBuf) {
+ pfd.buffersUsedInDrawCallSet.insert(params.indexBuf);
+ indexBufIdx = params.indexBuf - 1;
+ Q_ASSERT(indexBufIdx < buffers.count() && buffers[indexBufIdx].entryInUse());
}
- ensureBuffer(&constantData, &pfd.constant, "constant");
- if (!pfd.constant.buffer)
- return;
+ // Ensure buffers are created but do not copy the data here, leave that to endDrawCalls().
+ ensureBuffer(&buffers[vertexBufIdx]);
+ ensureBuffer(&buffers[constantBufIdx]);
+ if (indexBufIdx >= 0)
+ ensureBuffer(&buffers[indexBufIdx]);
// Set the CBV.
- if (cboOffset >= 0 && pfd.constant.buffer)
- commandList->SetGraphicsRootConstantBufferView(0, pfd.constant.buffer->GetGPUVirtualAddress() + cboOffset);
+ if (params.cboOffset >= 0) {
+ ID3D12Resource *cbuf = buffers[constantBufIdx].d[currentPFrameIndex].buffer.Get();
+ if (cbuf)
+ commandList->SetGraphicsRootConstantBufferView(0, cbuf->GetGPUVirtualAddress() + params.cboOffset);
+ }
// Set up vertex and index buffers.
- Q_ASSERT(pfd.vertex.buffer);
- Q_ASSERT(pfd.index.buffer || startIndexIndex < 0);
+ ID3D12Resource *vbuf = buffers[vertexBufIdx].d[currentPFrameIndex].buffer.Get();
+ ID3D12Resource *ibuf = indexBufIdx >= 0 && params.startIndexIndex >= 0
+ ? buffers[indexBufIdx].d[currentPFrameIndex].buffer.Get() : nullptr;
- if (mode != tframeData.drawingMode) {
+ if (params.mode != tframeData.drawingMode) {
D3D_PRIMITIVE_TOPOLOGY topology;
- switch (mode) {
+ switch (params.mode) {
case QSGGeometry::DrawPoints:
topology = D3D_PRIMITIVE_TOPOLOGY_POINTLIST;
break;
@@ -1791,27 +1815,27 @@ void QSGD3D12EnginePrivate::queueDraw(QSGGeometry::DrawingMode mode, int count,
topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
break;
default:
- qFatal("Unsupported drawing mode 0x%x", mode);
+ qFatal("Unsupported drawing mode 0x%x", params.mode);
break;
}
commandList->IASetPrimitiveTopology(topology);
- tframeData.drawingMode = mode;
+ tframeData.drawingMode = params.mode;
}
D3D12_VERTEX_BUFFER_VIEW vbv;
- vbv.BufferLocation = pfd.vertex.buffer->GetGPUVirtualAddress() + vboOffset;
- vbv.SizeInBytes = vboSize;
- vbv.StrideInBytes = vboStride;
+ vbv.BufferLocation = vbuf->GetGPUVirtualAddress() + params.vboOffset;
+ vbv.SizeInBytes = params.vboSize;
+ vbv.StrideInBytes = params.vboStride;
// must be set after the topology
commandList->IASetVertexBuffers(0, 1, &vbv);
- if (startIndexIndex >= 0 && !tframeData.indexBufferSet) {
- tframeData.indexBufferSet = true;
+ if (params.startIndexIndex >= 0 && ibuf && tframeData.currentIndexBuffer != params.indexBuf) {
+ tframeData.currentIndexBuffer = params.indexBuf;
D3D12_INDEX_BUFFER_VIEW ibv;
- ibv.BufferLocation = pfd.index.buffer->GetGPUVirtualAddress();
- ibv.SizeInBytes = indexData.size;
- ibv.Format = DXGI_FORMAT(indexFormat);
+ ibv.BufferLocation = ibuf->GetGPUVirtualAddress();
+ ibv.SizeInBytes = buffers[indexBufIdx].cpuDataRef.size;
+ ibv.Format = DXGI_FORMAT(params.indexFormat);
commandList->IASetIndexBuffer(&ibv);
}
@@ -1840,10 +1864,10 @@ void QSGD3D12EnginePrivate::queueDraw(QSGGeometry::DrawingMode mode, int count,
}
// Add the draw call.
- if (startIndexIndex >= 0)
- commandList->DrawIndexedInstanced(count, 1, startIndexIndex, 0, 0);
+ if (params.startIndexIndex >= 0)
+ commandList->DrawIndexedInstanced(params.count, 1, params.startIndexIndex, 0, 0);
else
- commandList->DrawInstanced(count, 1, 0, 0);
+ commandList->DrawInstanced(params.count, 1, 0, 0);
++tframeData.drawCount;
if (tframeData.drawCount == MAX_DRAW_CALLS_PER_LIST) {
@@ -1943,6 +1967,98 @@ template<class T> void syncEntryFlags(T *e, int flag, bool b)
e->flags &= ~flag;
}
+uint QSGD3D12EnginePrivate::genBuffer()
+{
+ return newId(&buffers);
+}
+
+void QSGD3D12EnginePrivate::releaseBuffer(uint id)
+{
+ if (!id)
+ return;
+
+ const int idx = id - 1;
+ Q_ASSERT(idx < buffers.count());
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("releasing buffer %u", id);
+
+ Buffer &b(buffers[idx]);
+ if (!b.entryInUse())
+ return;
+
+ // Do not null out and do not mark the entry reusable yet.
+ // Do that only when the frames potentially in flight have finished for sure.
+
+ for (int i = 0; i < frameInFlightCount; ++i) {
+ if (b.d[i].buffer)
+ deferredDelete(b.d[i].buffer);
+ }
+
+ QSet<PersistentFrameData::PendingRelease> *pendingReleasesSet = inFrame
+ ? &pframeData[currentPFrameIndex].pendingReleases
+ : &pframeData[(currentPFrameIndex + 1) % frameInFlightCount].outOfFramePendingReleases;
+
+ pendingReleasesSet->insert(PersistentFrameData::PendingRelease(PersistentFrameData::PendingRelease::TypeBuffer, id));
+}
+
+void QSGD3D12EnginePrivate::resetBuffer(uint id, const quint8 *data, int size)
+{
+ if (!inFrame) {
+ qWarning("%s: Cannot be called outside begin/endFrame", __FUNCTION__);
+ return;
+ }
+
+ Q_ASSERT(id);
+ const int idx = id - 1;
+ Q_ASSERT(idx < buffers.count() && buffers[idx].entryInUse());
+ Buffer &b(buffers[idx]);
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("reset buffer %u, size %d", id, size);
+
+ b.cpuDataRef.p = data;
+ b.cpuDataRef.size = size;
+
+ b.cpuDataRef.dirty.clear();
+ b.d[currentPFrameIndex].dirty.clear();
+
+ if (size > 0) {
+ const QPair<int, int> range = qMakePair(0, size);
+ b.cpuDataRef.dirty.append(range);
+ b.d[currentPFrameIndex].dirty.append(range);
+ }
+}
+
+void QSGD3D12EnginePrivate::addDirtyRange(DirtyList *dirty, int offset, int size, int bufferSize)
+{
+ // Bail out when the dirty list already spans the entire buffer.
+ if (!dirty->isEmpty()) {
+ if (dirty->at(0).first == 0 && dirty->at(0).second == bufferSize)
+ return;
+ }
+
+ const QPair<int, int> range = qMakePair(offset, size);
+ if (!dirty->contains(range))
+ dirty->append(range);
+}
+
+void QSGD3D12EnginePrivate::markBufferDirty(uint id, int offset, int size)
+{
+ if (!inFrame) {
+ qWarning("%s: Cannot be called outside begin/endFrame", __FUNCTION__);
+ return;
+ }
+
+ Q_ASSERT(id);
+ const int idx = id - 1;
+ Q_ASSERT(idx < buffers.count() && buffers[idx].entryInUse());
+ Buffer &b(buffers[idx]);
+
+ addDirtyRange(&b.cpuDataRef.dirty, offset, size, b.cpuDataRef.size);
+ addDirtyRange(&b.d[currentPFrameIndex].dirty, offset, size, b.cpuDataRef.size);
+}
+
uint QSGD3D12EnginePrivate::genTexture()
{
const uint id = newId(&textures);
@@ -2289,11 +2405,11 @@ void QSGD3D12EnginePrivate::releaseTexture(uint id)
deferredDelete(h);
}
- QSet<uint> *pendingTextureReleasesSet = inFrame
- ? &pframeData[currentPFrameIndex].pendingTextureReleases
- : &pframeData[(currentPFrameIndex + 1) % frameInFlightCount].outOfFramePendingTextureReleases;
+ QSet<PersistentFrameData::PendingRelease> *pendingReleasesSet = inFrame
+ ? &pframeData[currentPFrameIndex].pendingReleases
+ : &pframeData[(currentPFrameIndex + 1) % frameInFlightCount].outOfFramePendingReleases;
- pendingTextureReleasesSet->insert(id);
+ pendingReleasesSet->insert(PersistentFrameData::PendingRelease(PersistentFrameData::PendingRelease::TypeTexture, id));
}
SIZE_T QSGD3D12EnginePrivate::textureSRV(uint id) const
@@ -2425,7 +2541,7 @@ void QSGD3D12EnginePrivate::MipMapGen::queueGenerate(const Texture &t)
// ensure() call above resized, or, typically, due to a texture-dependent
// draw call earlier.
- engine->transitionResource(t.texture.Get(), engine->commandList.Get(),
+ engine->transitionResource(t.texture.Get(), engine->commandList,
D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
QSGD3D12EnginePrivate::PersistentFrameData &pfd(engine->pframeData[engine->currentPFrameIndex]);
@@ -2459,10 +2575,10 @@ void QSGD3D12EnginePrivate::MipMapGen::queueGenerate(const Texture &t)
engine->commandList->SetComputeRoot32BitConstants(2, 4, constants, 0);
engine->commandList->Dispatch(sz.width(), sz.height(), 1);
- engine->uavBarrier(t.texture.Get(), engine->commandList.Get());
+ engine->uavBarrier(t.texture.Get(), engine->commandList);
}
- engine->transitionResource(t.texture.Get(), engine->commandList.Get(),
+ engine->transitionResource(t.texture.Get(), engine->commandList,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
pfd.cbvSrvUavNextFreeDescriptorIndex += descriptorCount;
@@ -2510,6 +2626,10 @@ void QSGD3D12EnginePrivate::createRenderTarget(uint id, const QSize &size, const
Q_ASSERT(idx < renderTargets.count() && renderTargets[idx].entryInUse());
RenderTarget &rt(renderTargets[idx]);
+ rt.rtv = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
+ rt.dsv = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
+ rt.srv = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+
samples = qMax(1, samples);
ID3D12Resource *res = createColorBuffer(rt.rtv, size, clearColor, samples);
if (res)
@@ -2519,13 +2639,6 @@ void QSGD3D12EnginePrivate::createRenderTarget(uint id, const QSize &size, const
if (dsres)
rt.ds.Attach(dsres);
- rt.rtv = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
- rt.dsv = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
- rt.srv = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
-
- device->CreateRenderTargetView(rt.color.Get(), nullptr, rt.rtv);
- device->CreateDepthStencilView(rt.ds.Get(), nullptr, rt.dsv);
-
const bool multisample = rt.color->GetDesc().SampleDesc.Count > 1;
syncEntryFlags(&rt, RenderTarget::Multisample, multisample);
@@ -2554,6 +2667,9 @@ void QSGD3D12EnginePrivate::createRenderTarget(uint id, const QSize &size, const
device->CreateShaderResourceView(rt.colorResolve.Get(), nullptr, rt.srv);
}
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("created new render target %u, size %dx%d, samples %d", id, size.width(), size.height(), samples);
}
void QSGD3D12EnginePrivate::releaseRenderTarget(uint id)
@@ -2567,6 +2683,9 @@ void QSGD3D12EnginePrivate::releaseRenderTarget(uint id)
if (!rt.entryInUse())
return;
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("releasing render target %u", id);
+
if (rt.colorResolve) {
deferredDelete(rt.colorResolve);
rt.colorResolve = nullptr;
@@ -2602,9 +2721,9 @@ void QSGD3D12EnginePrivate::activateRenderTargetAsTexture(uint id)
if (rt.flags & RenderTarget::NeedsReadBarrier) {
rt.flags &= ~RenderTarget::NeedsReadBarrier;
if (rt.flags & RenderTarget::Multisample)
- resolveMultisampledTarget(rt.color.Get(), rt.colorResolve.Get(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, commandList.Get());
+ resolveMultisampledTarget(rt.color.Get(), rt.colorResolve.Get(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, commandList);
else
- transitionResource(rt.color.Get(), commandList.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
+ transitionResource(rt.color.Get(), commandList, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
}
tframeData.activeTextures.append(TransientFrameData::ActiveTexture::ActiveTexture(TransientFrameData::ActiveTexture::TypeRenderTarget, id));
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h
index 4127346fe6..84bb5a554e 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h
@@ -276,13 +276,13 @@ public:
void beginFrame();
void endFrame();
+ void beginLayer();
+ void endLayer();
- void resetVertexBuffer(const quint8 *data, int size);
- void markVertexBufferDirty(int offset, int size);
- void resetIndexBuffer(const quint8 *data, int size);
- void markIndexBufferDirty(int offset, int size);
- void resetConstantBuffer(const quint8 *data, int size);
- void markConstantBufferDirty(int offset, int size);
+ uint genBuffer();
+ void releaseBuffer(uint id);
+ void resetBuffer(uint id, const quint8 *data, int size);
+ void markBufferDirty(uint id, int offset, int size);
enum ClearFlag {
ClearDepth = 0x1,
@@ -300,10 +300,21 @@ public:
void finalizePipeline(const QSGD3D12PipelineState &pipelineState);
- void queueDraw(QSGGeometry::DrawingMode mode, int count,
- int vboOffset, int vboSize, int vboStride,
- int cboOffset,
- int startIndexIndex = -1, QSGD3D12Format indexFormat = FmtUnsignedShort);
+ struct DrawParams {
+ QSGGeometry::DrawingMode mode = QSGGeometry::DrawTriangles;
+ int count = 0;
+ uint vertexBuf = 0;
+ uint indexBuf = 0;
+ uint constantBuf = 0;
+ int vboOffset = 0;
+ int vboSize = 0;
+ int vboStride = 0;
+ int cboOffset = 0;
+ int startIndexIndex = -1;
+ QSGD3D12Format indexFormat = FmtUnsignedShort;
+ };
+
+ void queueDraw(const DrawParams &params);
void present();
void waitGPU();
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h
index c8f8402b67..40d1fdb510 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h
@@ -140,13 +140,13 @@ public:
void beginFrame();
void endFrame();
+ void beginLayer();
+ void endLayer();
- void resetVertexBuffer(const quint8 *data, int size);
- void markVertexBufferDirty(int offset, int size);
- void resetIndexBuffer(const quint8 *data, int size);
- void markIndexBufferDirty(int offset, int size);
- void resetConstantBuffer(const quint8 *data, int size);
- void markConstantBufferDirty(int offset, int size);
+ uint genBuffer();
+ void releaseBuffer(uint id);
+ void resetBuffer(uint id, const quint8 *data, int size);
+ void markBufferDirty(uint id, int offset, int size);
void queueViewport(const QRect &rect);
void queueScissor(const QRect &rect);
@@ -158,10 +158,7 @@ public:
void finalizePipeline(const QSGD3D12PipelineState &pipelineState);
- void queueDraw(QSGGeometry::DrawingMode mode, int count,
- int vboOffset, int vboSize, int vboStride,
- int cboOffset,
- int startIndexIndex, QSGD3D12Format indexFormat);
+ void queueDraw(const QSGD3D12Engine::DrawParams &params);
void present();
void waitGPU();
@@ -206,30 +203,15 @@ private:
ID3D12Resource *createBuffer(int size);
- struct CPUBufferRef {
- const quint8 *p = nullptr;
- quint32 size = 0;
- bool allDirty = true;
- QVector<QPair<int, int> > dirty;
- CPUBufferRef() { dirty.reserve(64); }
- };
+ typedef QVector<QPair<int, int> > DirtyList;
+ void addDirtyRange(DirtyList *dirty, int offset, int size, int bufferSize);
struct PersistentFrameData {
- struct ChangeTrackedBuffer {
- ComPtr<ID3D12Resource> buffer;
- QVector<QPair<int, int> > totalDirtyInFrame;
- quint32 dataSize = 0;
- };
- ChangeTrackedBuffer vertex;
- ChangeTrackedBuffer index;
- ChangeTrackedBuffer constant;
ComPtr<ID3D12DescriptorHeap> gpuCbvSrvUavHeap;
int gpuCbvSrvUavHeapSize;
int cbvSrvUavNextFreeDescriptorIndex;
QSet<uint> pendingTextureUploads;
QSet<uint> pendingTextureMipMap;
- QSet<uint> pendingTextureReleases;
- QSet<uint> outOfFramePendingTextureReleases;
struct DeleteQueueEntry {
ComPtr<ID3D12Resource> res;
ComPtr<ID3D12DescriptorHeap> descHeap;
@@ -237,17 +219,34 @@ private:
};
QVector<DeleteQueueEntry> deleteQueue;
QVector<DeleteQueueEntry> outOfFrameDeleteQueue;
+ QSet<uint> buffersUsedInDrawCallSet;
+ QSet<uint> buffersUsedInFrame;
+ struct PendingRelease {
+ enum Type {
+ TypeTexture,
+ TypeBuffer
+ };
+ Type type = TypeTexture;
+ uint id = 0;
+ PendingRelease(Type type, uint id) : type(type), id(id) { }
+ PendingRelease() { }
+ bool operator==(const PendingRelease &other) const { return type == other.type && id == other.id; }
+ };
+ QSet<PendingRelease> pendingReleases;
+ QSet<PendingRelease> outOfFramePendingReleases;
};
+ friend uint qHash(const PersistentFrameData::PendingRelease &pr, uint seed);
void deferredDelete(ComPtr<ID3D12Resource> res);
void deferredDelete(ComPtr<ID3D12DescriptorHeap> dh);
void deferredDelete(D3D12_CPU_DESCRIPTOR_HANDLE h);
- void markCPUBufferDirty(CPUBufferRef *dst, PersistentFrameData::ChangeTrackedBuffer *buf, int offset, int size);
- void ensureBuffer(CPUBufferRef *src, PersistentFrameData::ChangeTrackedBuffer *buf, const char *dbgstr);
- void updateBuffer(CPUBufferRef *src, PersistentFrameData::ChangeTrackedBuffer *buf, const char *dbgstr);
+ struct Buffer;
+ void ensureBuffer(Buffer *buf);
+ void updateBuffer(Buffer *buf);
- void beginDrawCalls(bool firstInFrame = false);
+ void beginDrawCalls();
+ void beginFrameDraw();
void endDrawCalls(bool lastInFrame = false);
static const int MAX_SWAP_CHAIN_BUFFER_COUNT = 4;
@@ -272,9 +271,9 @@ private:
D3D12_CPU_DESCRIPTOR_HANDLE defaultRTV[MAX_SWAP_CHAIN_BUFFER_COUNT];
ComPtr<ID3D12Resource> defaultDS;
D3D12_CPU_DESCRIPTOR_HANDLE defaultDSV;
- ComPtr<ID3D12CommandAllocator> commandAllocator[MAX_FRAME_IN_FLIGHT_COUNT];
+ ComPtr<ID3D12CommandAllocator> frameCommandAllocator[MAX_FRAME_IN_FLIGHT_COUNT];
ComPtr<ID3D12CommandAllocator> copyCommandAllocator;
- ComPtr<ID3D12GraphicsCommandList> commandList;
+ ComPtr<ID3D12GraphicsCommandList> frameCommandList;
ComPtr<ID3D12GraphicsCommandList> copyCommandList;
QSGD3D12CPUDescriptorHeapManager cpuDescHeapManager;
quint64 presentFrameIndex;
@@ -282,12 +281,11 @@ private:
QSGD3D12CPUWaitableFence *presentFence = nullptr;
QSGD3D12CPUWaitableFence *frameFence[MAX_FRAME_IN_FLIGHT_COUNT];
- CPUBufferRef vertexData;
- CPUBufferRef indexData;
- CPUBufferRef constantData;
-
PersistentFrameData pframeData[MAX_FRAME_IN_FLIGHT_COUNT];
int currentPFrameIndex;
+ ID3D12GraphicsCommandList *commandList = nullptr;
+ int activeLayers = 0;
+ int currentLayerDepth = 0;
struct PSOCacheEntry {
ComPtr<ID3D12PipelineState> pso;
@@ -329,7 +327,7 @@ private:
struct TransientFrameData {
QSGGeometry::DrawingMode drawingMode;
- bool indexBufferSet;
+ uint currentIndexBuffer;
struct ActiveTexture {
enum Type {
TypeTexture,
@@ -384,8 +382,40 @@ private:
QVector<RenderTarget> renderTargets;
uint currentRenderTarget;
+
+ struct CPUBufferRef {
+ const quint8 *p = nullptr;
+ quint32 size = 0;
+ DirtyList dirty;
+ CPUBufferRef() { dirty.reserve(16); }
+ };
+
+ struct Buffer {
+ enum Flag {
+ EntryInUse = 0x01
+ };
+ int flags = 0;
+ bool entryInUse() const { return flags & EntryInUse; }
+ struct InFlightData {
+ ComPtr<ID3D12Resource> buffer;
+ DirtyList dirty;
+ quint32 dataSize = 0;
+ quint32 resourceSize = 0;
+ InFlightData() { dirty.reserve(16); }
+ };
+ InFlightData d[MAX_FRAME_IN_FLIGHT_COUNT];
+ CPUBufferRef cpuDataRef;
+ };
+
+ QVector<Buffer> buffers;
};
+inline uint qHash(const QSGD3D12EnginePrivate::PersistentFrameData::PendingRelease &pr, uint seed = 0)
+{
+ Q_UNUSED(seed);
+ return pr.id + pr.type;
+}
+
QT_END_NAMESPACE
#endif
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12layer.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12layer.cpp
index 37cdc89ffe..6623c51f5c 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12layer.cpp
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12layer.cpp
@@ -38,27 +38,45 @@
****************************************************************************/
#include "qsgd3d12layer_p.h"
+#include "qsgd3d12rendercontext_p.h"
+#include "qsgd3d12engine_p.h"
+#include "qsgd3d12renderer_p.h"
QT_BEGIN_NAMESPACE
+// NOTE: Avoid categorized logging. It is slow.
+
+#define DECLARE_DEBUG_VAR(variable) \
+ static bool debug_ ## variable() \
+ { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; }
+
+DECLARE_DEBUG_VAR(render)
+
QSGD3D12Layer::QSGD3D12Layer(QSGD3D12RenderContext *rc)
: m_rc(rc)
{
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("new layer %p", this);
}
QSGD3D12Layer::~QSGD3D12Layer()
{
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("destroying layer %p", this);
+
cleanup();
}
+// QSGTexture
+
int QSGD3D12Layer::textureId() const
{
- return 0;
+ return m_rt; // not a texture id per se but will do
}
QSize QSGD3D12Layer::textureSize() const
{
- return QSize();
+ return m_size;
}
bool QSGD3D12Layer::hasAlphaChannel() const
@@ -68,43 +86,97 @@ bool QSGD3D12Layer::hasAlphaChannel() const
bool QSGD3D12Layer::hasMipmaps() const
{
- // ###
+ // mipmapped layers are not supported for now
return false;
}
QRectF QSGD3D12Layer::normalizedTextureSubRect() const
{
- return QRectF(0, 0, 1, 1); // ### mirror h/v
+ // (0, 0) is the top-left corner, unlike OpenGL. Hence the inversion of
+ // m_mirrorVertical, as opposed to the GL version.
+ return QRectF(m_mirrorHorizontal ? 1 : 0,
+ m_mirrorVertical ? 1 : 0,
+ m_mirrorHorizontal ? -1 : 1,
+ m_mirrorVertical ? -1 : 1);
}
void QSGD3D12Layer::bind()
{
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("layer %p bind rt=%u", this, m_rt);
+
+ // ###
+ m_rc->engine()->activateRenderTargetAsTexture(m_rt);
}
+// QSGDynamicTexture
+
bool QSGD3D12Layer::updateTexture()
{
- return false;
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("layer %p updateTexture", this);
+
+ const bool doUpdate = (m_live || m_updateContentPending) && m_dirtyTexture;
+
+ if (doUpdate)
+ updateContent();
+
+ if (m_updateContentPending) {
+ m_updateContentPending = false;
+ emit scheduledUpdateCompleted();
+ }
+
+ return doUpdate;
}
+// QSGLayer
+
void QSGD3D12Layer::setItem(QSGNode *item)
{
+ if (m_item == item)
+ return;
+ if (m_live && !item)
+ resetRenderTarget();
+
+ m_item = item;
+ markDirtyTexture();
}
void QSGD3D12Layer::setRect(const QRectF &rect)
{
+ if (m_rect == rect)
+ return;
+ m_rect = rect;
+ markDirtyTexture();
}
void QSGD3D12Layer::setSize(const QSize &size)
{
+ if (m_size == size)
+ return;
+
+ if (m_live && size.isNull())
+ resetRenderTarget();
+ m_size = size;
+ markDirtyTexture();
}
void QSGD3D12Layer::scheduleUpdate()
{
+ if (m_updateContentPending)
+ return;
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("layer %p scheduleUpdate", this);
+
+ m_updateContentPending = true;
+ if (m_dirtyTexture)
+ emit updateRequested();
}
QImage QSGD3D12Layer::toImage() const
@@ -115,42 +187,56 @@ QImage QSGD3D12Layer::toImage() const
void QSGD3D12Layer::setLive(bool live)
{
+ if (m_live == live)
+ return;
+ if (live && (!m_item || m_size.isNull()))
+ resetRenderTarget();
+
+ m_live = live;
+ markDirtyTexture();
}
void QSGD3D12Layer::setRecursive(bool recursive)
{
-
+ m_recursive = recursive;
}
void QSGD3D12Layer::setFormat(uint format)
{
-
+ Q_UNUSED(format);
}
void QSGD3D12Layer::setHasMipmaps(bool mipmap)
{
-
+ // mipmapped layers are not supported for now
+ Q_UNUSED(mipmap);
}
void QSGD3D12Layer::setDevicePixelRatio(qreal ratio)
{
-
+ m_dpr = ratio;
}
void QSGD3D12Layer::setMirrorHorizontal(bool mirror)
{
-
+ m_mirrorHorizontal = mirror;
}
void QSGD3D12Layer::setMirrorVertical(bool mirror)
{
-
+ m_mirrorVertical = mirror;
}
void QSGD3D12Layer::markDirtyTexture()
{
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("layer %p markDirtyTexture", this);
+
+ m_dirtyTexture = true;
+ if (m_live || m_updateContentPending)
+ emit updateRequested();
}
void QSGD3D12Layer::invalidated()
@@ -160,6 +246,84 @@ void QSGD3D12Layer::invalidated()
void QSGD3D12Layer::cleanup()
{
+ if (!m_renderer && !m_rt)
+ return;
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("layer %p cleanup renderer=%p rt=%u", this, m_renderer, m_rt);
+
+ delete m_renderer;
+ m_renderer = nullptr;
+
+ resetRenderTarget();
+}
+
+void QSGD3D12Layer::resetRenderTarget()
+{
+ if (!m_rt)
+ return;
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("layer %p resetRenderTarget rt=%u", this, m_rt);
+
+ m_rc->engine()->releaseRenderTarget(m_rt);
+ m_rt = 0;
+}
+
+void QSGD3D12Layer::updateContent()
+{
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("layer %p updateContent", this);
+
+ if (!m_item || m_size.isNull()) {
+ resetRenderTarget();
+ m_dirtyTexture = false;
+ return;
+ }
+
+ QSGNode *root = m_item;
+ while (root->firstChild() && root->type() != QSGNode::RootNodeType)
+ root = root->firstChild();
+
+ if (root->type() != QSGNode::RootNodeType)
+ return;
+
+ if (!m_renderer) {
+ m_renderer = m_rc->createRenderer();
+ static_cast<QSGD3D12Renderer *>(m_renderer)->turnToLayerRenderer();
+ connect(m_renderer, &QSGRenderer::sceneGraphChanged, this, &QSGD3D12Layer::markDirtyTexture);
+ }
+
+ m_renderer->setDevicePixelRatio(m_dpr);
+ m_renderer->setRootNode(static_cast<QSGRootNode *>(root));
+
+ if (!m_rt || m_rtSize != m_size) {
+ // ### recursive, multisample
+
+ if (m_rt)
+ resetRenderTarget();
+
+ m_rt = m_rc->engine()->genRenderTarget();
+ m_rtSize = m_size;
+ m_rc->engine()->createRenderTarget(m_rt, m_rtSize, QVector4D(0, 0, 0, 0), 0);
+ }
+
+ m_dirtyTexture = false;
+
+ m_renderer->setDeviceRect(m_size);
+ m_renderer->setViewportRect(m_size);
+ QRectF mirrored(m_mirrorHorizontal ? m_rect.right() : m_rect.left(),
+ m_mirrorVertical ? m_rect.bottom() : m_rect.top(),
+ m_mirrorHorizontal ? -m_rect.width() : m_rect.width(),
+ m_mirrorVertical ? -m_rect.height() : m_rect.height());
+ m_renderer->setProjectionMatrixToRect(mirrored);
+ m_renderer->setClearColor(Qt::transparent);
+
+ // ### recursive, multisample
+ m_renderer->renderScene(m_rt);
+
+ if (m_recursive)
+ markDirtyTexture(); // Continuously update if 'live' and 'recursive'.
}
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12layer_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12layer_p.h
index 55883a1fad..d167cf4f66 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12layer_p.h
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12layer_p.h
@@ -59,6 +59,8 @@ class QSGD3D12RenderContext;
class QSGD3D12Layer : public QSGLayer
{
+ Q_OBJECT
+
public:
QSGD3D12Layer(QSGD3D12RenderContext *rc);
~QSGD3D12Layer();
@@ -84,13 +86,30 @@ public:
void setDevicePixelRatio(qreal ratio) override;
void setMirrorHorizontal(bool mirror) override;
void setMirrorVertical(bool mirror) override;
+
+public Q_SLOTS:
void markDirtyTexture() override;
void invalidated() override;
private:
void cleanup();
+ void resetRenderTarget();
+ void updateContent();
QSGD3D12RenderContext *m_rc;
+ uint m_rt = 0;
+ QSize m_rtSize;
+ QSize m_size;
+ QRectF m_rect;
+ QSGNode *m_item = nullptr;
+ QSGRenderer *m_renderer = nullptr;
+ float m_dpr = 1;
+ bool m_mirrorHorizontal = false;
+ bool m_mirrorVertical = true;
+ bool m_live = true;
+ bool m_recursive = false;
+ bool m_dirtyTexture = true;
+ bool m_updateContentPending = false;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp
index f6bd725e44..99e7bfbfc6 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp
@@ -77,15 +77,24 @@ QSGD3D12Renderer::QSGD3D12Renderer(QSGRenderContext *context)
m_freshPipelineState.shaders.rootSig.textureViews.reserve(4);
}
+QSGD3D12Renderer::~QSGD3D12Renderer()
+{
+ if (m_engine) {
+ m_engine->releaseBuffer(m_vertexBuf);
+ m_engine->releaseBuffer(m_indexBuf);
+ m_engine->releaseBuffer(m_constantBuf);
+ }
+}
+
void QSGD3D12Renderer::renderScene(GLuint fboId)
{
- Q_UNUSED(fboId);
+ m_renderTarget = fboId;
- struct B : public QSGBindable {
+ struct DummyBindable : public QSGBindable {
void bind() const { }
} bindable;
- QSGRenderer::renderScene(bindable);
+ QSGRenderer::renderScene(bindable); // calls back render()
}
// Search through the node set and remove nodes that are descendants of other
@@ -207,7 +216,10 @@ void QSGD3D12Renderer::render()
{
QSGD3D12RenderContext *rc = static_cast<QSGD3D12RenderContext *>(context());
m_engine = rc->engine();
- m_engine->beginFrame();
+ if (!m_layerRenderer)
+ m_engine->beginFrame();
+ else
+ m_engine->beginLayer();
m_activeScissorRect = QRect();
@@ -226,9 +238,22 @@ void QSGD3D12Renderer::render()
buildRenderList(rootNode(), nullptr);
- m_engine->resetVertexBuffer(m_vboData.data(), m_vboData.size());
- m_engine->resetIndexBuffer(m_iboData.data(), m_iboData.size());
- m_engine->resetConstantBuffer(m_cboData.data(), m_cboData.size());
+ if (!m_vertexBuf)
+ m_vertexBuf = m_engine->genBuffer();
+ m_engine->resetBuffer(m_vertexBuf, m_vboData.data(), m_vboData.size());
+
+ if (!m_constantBuf)
+ m_constantBuf = m_engine->genBuffer();
+ m_engine->resetBuffer(m_constantBuf, m_cboData.data(), m_cboData.size());
+
+ if (m_iboData.size()) {
+ if (!m_indexBuf)
+ m_indexBuf = m_engine->genBuffer();
+ m_engine->resetBuffer(m_indexBuf, m_iboData.data(), m_iboData.size());
+ } else if (m_indexBuf) {
+ m_engine->releaseBuffer(m_indexBuf);
+ m_indexBuf = 0;
+ }
if (Q_UNLIKELY(debug_build())) {
qDebug("renderList: %d elements in total", m_renderList.size());
@@ -303,9 +328,10 @@ void QSGD3D12Renderer::render()
m_nodeDirtyMap.clear();
// Finalize buffers and execute commands.
- m_engine->endFrame();
-
- m_engine = nullptr;
+ if (!m_layerRenderer)
+ m_engine->endFrame();
+ else
+ m_engine->endLayer();
}
void QSGD3D12Renderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
@@ -361,7 +387,7 @@ void QSGD3D12Renderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
void QSGD3D12Renderer::renderElements()
{
- m_engine->queueSetRenderTarget();
+ m_engine->queueSetRenderTarget(m_renderTarget);
m_engine->queueViewport(viewportRect());
m_engine->queueClearRenderTarget(clearColor());
m_engine->queueClearDepthStencil(1, 0, QSGD3D12Engine::ClearDepth | QSGD3D12Engine::ClearStencil);
@@ -473,7 +499,7 @@ void QSGD3D12Renderer::renderElement(int elementIndex)
cboPtr);
if (updRes.testFlag(QSGD3D12Material::UpdatedConstantBuffer))
- m_engine->markConstantBufferDirty(e.cboOffset, e.cboSize);
+ m_engine->markBufferDirty(m_constantBuf, e.cboOffset, e.cboSize);
if (updRes.testFlag(QSGD3D12Material::UpdatedBlendFactor))
m_engine->queueSetBlendFactor(extraState.blendFactor);
@@ -524,20 +550,29 @@ void QSGD3D12Renderer::setInputLayout(const QSGGeometry *g, QSGD3D12PipelineStat
void QSGD3D12Renderer::queueDrawCall(const QSGGeometry *g, const QSGD3D12Renderer::Element &e)
{
+ QSGD3D12Engine::DrawParams dp;
+ dp.mode = QSGGeometry::DrawingMode(g->drawingMode());
+ dp.vertexBuf = m_vertexBuf;
+ dp.constantBuf = m_constantBuf;
+ dp.vboOffset = e.vboOffset;
+ dp.vboSize = g->vertexCount() * g->sizeOfVertex();
+ dp.vboStride = g->sizeOfVertex();
+ dp.cboOffset = e.cboOffset;
+
if (e.iboOffset >= 0) {
const QSGGeometry::Type indexType = QSGGeometry::Type(g->indexType());
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->vertexCount() * g->sizeOfVertex(), g->sizeOfVertex(),
- e.cboOffset,
- e.iboOffset / e.iboStride, indexFormat);
+ dp.count = g->indexCount();
+ dp.indexBuf = m_indexBuf;
+ dp.startIndexIndex = e.iboOffset / e.iboStride;
+ dp.indexFormat = indexFormat;
} else {
- m_engine->queueDraw(QSGGeometry::DrawingMode(g->drawingMode()), g->vertexCount(),
- e.vboOffset, g->vertexCount() * g->sizeOfVertex(), g->sizeOfVertex(),
- e.cboOffset);
+ dp.count = g->vertexCount();
}
+
+ m_engine->queueDraw(dp);
}
void QSGD3D12Renderer::setupClipping(const QSGGeometryNode *gn, int elementIndex)
@@ -665,7 +700,7 @@ void QSGD3D12Renderer::renderStencilClip(const QSGClipNode *clip, int elementInd
Q_ASSERT(ce.cboSize > 0);
quint8 *p = m_cboData.data() + ce.cboOffset;
memcpy(p, m.constData(), 16 * sizeof(float));
- m_engine->markConstantBufferDirty(ce.cboOffset, ce.cboSize);
+ m_engine->markBufferDirty(m_constantBuf, ce.cboOffset, ce.cboSize);
queueDrawCall(g, ce);
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer_p.h
index e1dc6bf78e..adb2252f80 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer_p.h
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer_p.h
@@ -62,11 +62,14 @@ class QSGD3D12Renderer : public QSGRenderer
{
public:
QSGD3D12Renderer(QSGRenderContext *context);
+ ~QSGD3D12Renderer();
void renderScene(GLuint fboId) override;
void render() override;
void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override;
+ void turnToLayerRenderer() { m_layerRenderer = true; }
+
private:
void updateMatrices(QSGNode *node, QSGTransformNode *xform);
void updateOpacities(QSGNode *node, float inheritedOpacity);
@@ -90,6 +93,7 @@ private:
void queueDrawCall(const QSGGeometry *g, const Element &e);
+ bool m_layerRenderer = false;
QSet<QSGNode *> m_dirtyTransformNodes;
QSet<QSGNode *> m_dirtyOpacityNodes;
QBitArray m_opaqueElements;
@@ -99,7 +103,10 @@ private:
QDataBuffer<quint8> m_iboData;
QDataBuffer<quint8> m_cboData;
QDataBuffer<Element> m_renderList;
- QSGD3D12Engine *m_engine;
+ uint m_vertexBuf = 0;
+ uint m_indexBuf = 0;
+ uint m_constantBuf = 0;
+ QSGD3D12Engine *m_engine = nullptr;
QSGMaterialType *m_lastMaterialType = nullptr;
QSGD3D12PipelineState m_pipelineState;
@@ -111,6 +118,8 @@ private:
QRect m_activeScissorRect;
QRect m_lastDeviceRect;
bool m_projectionChangedDueToDeviceSize;
+
+ uint m_renderTarget = 0;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgbasicimagenode.cpp b/src/quick/scenegraph/qsgbasicimagenode.cpp
index 0f3fef5dc7..0d33e2e24a 100644
--- a/src/quick/scenegraph/qsgbasicimagenode.cpp
+++ b/src/quick/scenegraph/qsgbasicimagenode.cpp
@@ -72,6 +72,7 @@ QSGBasicImageNode::QSGBasicImageNode()
, m_mirror(false)
, m_dirtyGeometry(false)
, m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
+ , m_dynamicTexture(nullptr)
{
setGeometry(&m_geometry);
@@ -162,9 +163,19 @@ void QSGBasicImageNode::preprocess()
QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(materialTexture());
if (t) {
doDirty = t->updateTexture();
- if (doDirty)
- updateGeometry();
+ if (doDirty) {
+ // The geometry may need updating. This is expensive however, so do
+ // it only when something relevant has changed.
+ if (t != m_dynamicTexture
+ || t->textureSize() != m_dynamicTextureSize
+ || t->normalizedTextureSubRect() != m_dynamicTextureSubRect) {
+ updateGeometry();
+ m_dynamicTextureSize = t->textureSize();
+ m_dynamicTextureSubRect = t->normalizedTextureSubRect();
+ }
+ }
}
+ m_dynamicTexture = t;
if (updateMaterialBlending())
doDirty = true;
diff --git a/src/quick/scenegraph/qsgbasicimagenode_p.h b/src/quick/scenegraph/qsgbasicimagenode_p.h
index 6820db7fe3..288b6c23f0 100644
--- a/src/quick/scenegraph/qsgbasicimagenode_p.h
+++ b/src/quick/scenegraph/qsgbasicimagenode_p.h
@@ -89,6 +89,10 @@ protected:
uint m_dirtyGeometry : 1;
QSGGeometry m_geometry;
+
+ QSGDynamicTexture *m_dynamicTexture;
+ QSize m_dynamicTextureSize;
+ QRectF m_dynamicTextureSubRect;
};
QT_END_NAMESPACE
diff --git a/tests/manual/nodetypes/Layers.qml b/tests/manual/nodetypes/Layers.qml
new file mode 100644
index 0000000000..52c8fa8144
--- /dev/null
+++ b/tests/manual/nodetypes/Layers.qml
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** 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 {
+ color: "lightGray"
+ anchors.fill: parent
+ anchors.margins: 10
+
+ Row {
+ anchors.fill: parent
+ anchors.margins: 10
+ Rectangle {
+ color: "red"
+// ColorAnimation on color {
+// from: "black"
+// to: "white"
+// duration: 2000
+// loops: Animation.Infinite
+// }
+ width: 300
+ height: 100
+ layer.enabled: true
+ Text { text: "this is in a layer, going through an offscreen render target" }
+ clip: true
+ Rectangle {
+ color: "lightGreen"
+ width: 50
+ height: 50
+ x: 275
+ y: 75
+ }
+ }
+ Rectangle {
+ color: "white"
+ width: 300
+ height: 100
+ Text { text: "this is not a layer" }
+ }
+ Rectangle {
+ color: "green"
+ width: 300
+ height: 100
+ layer.enabled: true
+ Text { text: "this is another layer" }
+ Rectangle {
+ border.width: 4
+ border.color: "black"
+ anchors.centerIn: parent
+ width: 150
+ height: 50
+ layer.enabled: true
+ Text {
+ anchors.centerIn: parent
+ text: "layer in a layer"
+ }
+ }
+ Image {
+ source: "qrc:/face-smile.png"
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/manual/nodetypes/main.qml b/tests/manual/nodetypes/main.qml
index b083bcc648..fe75e2d948 100644
--- a/tests/manual/nodetypes/main.qml
+++ b/tests/manual/nodetypes/main.qml
@@ -65,5 +65,8 @@ Item {
if (event.key === Qt.Key_A)
loader.source = "qrc:/Animators.qml";
+
+ if (event.key === Qt.Key_L)
+ loader.source = "qrc:/Layers.qml";
}
}
diff --git a/tests/manual/nodetypes/nodetypes.cpp b/tests/manual/nodetypes/nodetypes.cpp
index 3e82fc7d04..aaa641f300 100644
--- a/tests/manual/nodetypes/nodetypes.cpp
+++ b/tests/manual/nodetypes/nodetypes.cpp
@@ -66,6 +66,7 @@ int main(int argc, char **argv)
qDebug(" [I] - Images");
qDebug(" [T] - Text");
qDebug(" [A] - Render thread Animator");
+ qDebug(" [L] - Layers");
qDebug("\nPress S to stop the currently running test\n");
Helper helper;
diff --git a/tests/manual/nodetypes/nodetypes.pro b/tests/manual/nodetypes/nodetypes.pro
index 713c48064f..a1633dcf22 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 LotsOfRects.qml Images.qml Text.qml Animators.qml
+OTHER_FILES += main.qml Rects.qml LotsOfRects.qml Images.qml Text.qml Animators.qml Layers.qml
diff --git a/tests/manual/nodetypes/nodetypes.qrc b/tests/manual/nodetypes/nodetypes.qrc
index 2f1f2a2bbc..b10fceef24 100644
--- a/tests/manual/nodetypes/nodetypes.qrc
+++ b/tests/manual/nodetypes/nodetypes.qrc
@@ -6,6 +6,7 @@
<file>Images.qml</file>
<file>Text.qml</file>
<file>Animators.qml</file>
+ <file>Layers.qml</file>
<file>qt.png</file>
<file>face-smile.png</file>
<file>shadow.png</file>