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