aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@theqtcompany.com>2016-04-03 20:12:17 +0200
committerLaszlo Agocs <laszlo.agocs@theqtcompany.com>2016-04-11 10:12:15 +0000
commit7917135a7198df748adbc519a3c7930f9c1468af (patch)
tree6bcc423b1e5b351849ece52c09a1a6a75e04dbad
parentf1809d6f3d06f3ce8753914670e333e8e5cd68b0 (diff)
D3D12: Add support for layers
Recursive and multisample support is left as a future exercise. Mipmapping is not supported and is not planned for the time being. Layers cannot currently be rendered on their own, although they have their own independent render targets. Starting a layer just starts the next frame early, putting all commands to the normal command list of that frame. Thus this is not yet fully suitable for implementing grabs. Buffer handling is revised to support multiple vertex, index or constant buffers. This is essential since we are going to have multiple renderer instances (and may also be needed for a more complex batching renderer in the future) QSGBasicImageNode::preprocess is changed not to regenerate the geometry on every dynamic texture change. In most cases only the material needs to be dirtied. Rebuilding the geometry is only necessary when the normalized subrect or similar changes. Change-Id: Id088c15d1b75022b54c1f8bff1656d2cd68fa7cc Reviewed-by: Andy Nichols <andy.nichols@theqtcompany.com>
-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>