diff options
-rw-r--r-- | src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp | 5 | ||||
-rw-r--r-- | src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp | 593 | ||||
-rw-r--r-- | src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h | 31 | ||||
-rw-r--r-- | src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h | 106 | ||||
-rw-r--r-- | src/quick/scenegraph/adaptations/d3d12/qsgd3d12layer.cpp | 186 | ||||
-rw-r--r-- | src/quick/scenegraph/adaptations/d3d12/qsgd3d12layer_p.h | 19 | ||||
-rw-r--r-- | src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp | 75 | ||||
-rw-r--r-- | src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer_p.h | 11 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgbasicimagenode.cpp | 15 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgbasicimagenode_p.h | 4 | ||||
-rw-r--r-- | tests/manual/nodetypes/Layers.qml | 106 | ||||
-rw-r--r-- | tests/manual/nodetypes/main.qml | 3 | ||||
-rw-r--r-- | tests/manual/nodetypes/nodetypes.cpp | 1 | ||||
-rw-r--r-- | tests/manual/nodetypes/nodetypes.pro | 2 | ||||
-rw-r--r-- | tests/manual/nodetypes/nodetypes.qrc | 1 |
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 ¶ms) { - 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(©CommandList)))) { @@ -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 ¶ms) { 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 ¶ms); 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 ¶ms); 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> |