1 /****************************************************************************
2 **
3 ** Copyright (C) 2008-2012 NVIDIA Corporation.
4 ** Copyright (C) 2019 The Qt Company Ltd.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of Qt Quick 3D.
8 **
9 ** $QT_BEGIN_LICENSE:GPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU
20 ** General Public License version 3 or (at your option) any later version
21 ** approved by the KDE Free Qt Foundation. The licenses are as published by
22 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
23 ** included in the packaging of this file. Please review the following
24 ** information to ensure the GNU General Public License requirements will
25 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
26 **
27 ** $QT_END_LICENSE$
28 **
29 ****************************************************************************/
30 
31 #include "qssgrenderresourcemanager_p.h"
32 
33 #include <QtQuick3DRender/private/qssgrendercontext_p.h>
34 #include <QtQuick3DRender/private/qssgrenderframebuffer_p.h>
35 #include <QtQuick3DRender/private/qssgrenderrenderbuffer_p.h>
36 #include <QtQuick3DRender/private/qssgrendertexture2d_p.h>
37 #include <QtQuick3DRender/private/qssgrendertexturecube_p.h>
38 
39 QT_BEGIN_NAMESPACE
40 
41 template <typename T>
replaceWithLast(QVector<T> & vector,int index)42 static void replaceWithLast(QVector<T> &vector, int index)
43 {
44     vector[index] = vector.back();
45     vector.pop_back();
46 }
47 
48 
QSSGResourceManager(const QSSGRef<QSSGRenderContext> & ctx)49 QSSGResourceManager::QSSGResourceManager(const QSSGRef<QSSGRenderContext> &ctx)
50     : renderContext(ctx)
51 {
52 }
53 
54 QSSGResourceManager::~QSSGResourceManager() = default;
55 
allocateFrameBuffer()56 QSSGRef<QSSGRenderFrameBuffer> QSSGResourceManager::allocateFrameBuffer()
57 {
58     if (freeFrameBuffers.empty() == true) {
59         auto newBuffer = new QSSGRenderFrameBuffer(renderContext);
60         freeFrameBuffers.push_back(newBuffer);
61     }
62     auto retval = freeFrameBuffers.back();
63     freeFrameBuffers.pop_back();
64     return retval;
65 }
66 
release(const QSSGRef<QSSGRenderFrameBuffer> & inBuffer)67 void QSSGResourceManager::release(const QSSGRef<QSSGRenderFrameBuffer> &inBuffer)
68 {
69     if (inBuffer->hasAnyAttachment()) {
70         // Ensure the framebuffer has no attachments.
71         inBuffer->attach(QSSGRenderFrameBufferAttachment::Color0, QSSGRenderTextureOrRenderBuffer());
72         inBuffer->attach(QSSGRenderFrameBufferAttachment::Color1, QSSGRenderTextureOrRenderBuffer());
73         inBuffer->attach(QSSGRenderFrameBufferAttachment::Color2, QSSGRenderTextureOrRenderBuffer());
74         inBuffer->attach(QSSGRenderFrameBufferAttachment::Color3, QSSGRenderTextureOrRenderBuffer());
75         inBuffer->attach(QSSGRenderFrameBufferAttachment::Color4, QSSGRenderTextureOrRenderBuffer());
76         inBuffer->attach(QSSGRenderFrameBufferAttachment::Color5, QSSGRenderTextureOrRenderBuffer());
77         inBuffer->attach(QSSGRenderFrameBufferAttachment::Color6, QSSGRenderTextureOrRenderBuffer());
78         inBuffer->attach(QSSGRenderFrameBufferAttachment::Color7, QSSGRenderTextureOrRenderBuffer());
79         inBuffer->attach(QSSGRenderFrameBufferAttachment::Depth, QSSGRenderTextureOrRenderBuffer());
80         inBuffer->attach(QSSGRenderFrameBufferAttachment::Stencil, QSSGRenderTextureOrRenderBuffer());
81         if (renderContext->supportsDepthStencil())
82             inBuffer->attach(QSSGRenderFrameBufferAttachment::DepthStencil, QSSGRenderTextureOrRenderBuffer());
83     }
84 #ifdef _DEBUG
85     auto theFind = std::find(freeFrameBuffers.begin(), freeFrameBuffers.end(), inBuffer);
86     Q_ASSERT(theFind == freeFrameBuffers.end());
87 #endif
88     freeFrameBuffers.push_back(inBuffer);
89 }
90 
allocateRenderBuffer(qint32 inWidth,qint32 inHeight,QSSGRenderRenderBufferFormat inBufferFormat)91 QSSGRef<QSSGRenderRenderBuffer> QSSGResourceManager::allocateRenderBuffer(qint32 inWidth, qint32 inHeight, QSSGRenderRenderBufferFormat inBufferFormat)
92 {
93     Q_ASSERT(inWidth >= 0 && inHeight >= 0);
94     // Look for one of this specific size and format.
95     int existingMatchIdx = freeRenderBuffers.size();
96     for (int idx = 0, end = existingMatchIdx; idx < end; ++idx) {
97         auto theBuffer = freeRenderBuffers[idx];
98         QSize theDims = theBuffer->size();
99         QSSGRenderRenderBufferFormat theFormat = theBuffer->storageFormat();
100         if (theDims.width() == inWidth && theDims.height() == inHeight && theFormat == inBufferFormat) {
101             // Replace idx with last for efficient erasure (that reorders the vector).
102             replaceWithLast(freeRenderBuffers, idx);
103             return theBuffer;
104         }
105         if (theFormat == inBufferFormat)
106             existingMatchIdx = idx;
107     }
108     // If a specific exact match couldn't be found, just use the buffer with
109     // the same format and resize it.
110     if (existingMatchIdx < freeRenderBuffers.size()) {
111         auto theBuffer = freeRenderBuffers[existingMatchIdx];
112         replaceWithLast(freeRenderBuffers, existingMatchIdx);
113         theBuffer->setSize(QSize(inWidth, inHeight));
114         return theBuffer;
115     }
116 
117     auto theBuffer = new QSSGRenderRenderBuffer(renderContext, inBufferFormat, inWidth, inHeight);
118     return theBuffer;
119 }
120 
release(const QSSGRef<QSSGRenderRenderBuffer> & inBuffer)121 void QSSGResourceManager::release(const QSSGRef<QSSGRenderRenderBuffer> &inBuffer)
122 {
123     freeRenderBuffers.push_back(inBuffer);
124 }
125 
setupAllocatedTexture(QSSGRef<QSSGRenderTexture2D> inTexture)126 QSSGRef<QSSGRenderTexture2D> QSSGResourceManager::setupAllocatedTexture(QSSGRef<QSSGRenderTexture2D> inTexture)
127 {
128     inTexture->setMinFilter(QSSGRenderTextureMinifyingOp::Linear);
129     inTexture->setMagFilter(QSSGRenderTextureMagnifyingOp::Linear);
130     return inTexture;
131 }
132 
allocateTexture2D(qint32 inWidth,qint32 inHeight,QSSGRenderTextureFormat inTextureFormat,qint32 inSampleCount,bool immutable)133 QSSGRef<QSSGRenderTexture2D> QSSGResourceManager::allocateTexture2D(qint32 inWidth, qint32 inHeight, QSSGRenderTextureFormat inTextureFormat, qint32 inSampleCount, bool immutable)
134 {
135     Q_ASSERT(inWidth >= 0 && inHeight >= 0 && inSampleCount >= 0);
136     bool inMultisample = inSampleCount > 1 && renderContext->supportsMultisampleTextures();
137     for (qint32 idx = 0, end = freeTextures.size(); idx < end; ++idx) {
138         auto theTexture = freeTextures[idx];
139         QSSGTextureDetails theDetails = theTexture->textureDetails();
140         if (theDetails.width == inWidth && theDetails.height == inHeight && inTextureFormat == theDetails.format
141                 && theTexture->sampleCount() == inSampleCount) {
142             replaceWithLast(freeTextures, idx);
143             return setupAllocatedTexture(theTexture);
144         }
145     }
146     // else resize an existing texture.  This is very expensive
147     // note that MSAA textures are not resizable ( in GLES )
148     /*
149         if ( !freeTextures.empty() && !inMultisample )
150         {
151                 QSSGRenderTexture2D* theTexture = freeTextures.back();
152                 freeTextures.pop_back();
153 
154                 // note we could re-use a former MSAA texture
155                 // this causes a entiere destroy of the previous texture object
156                 theTexture->SetTextureData( QSSGByteView(), 0, inWidth, inHeight, inTextureFormat
157         );
158 
159                 return SetupAllocatedTexture( *theTexture );
160         }*/
161     // else create a new texture.
162     auto theTexture = new QSSGRenderTexture2D(renderContext);
163 
164     if (inMultisample)
165         theTexture->setTextureDataMultisample(inSampleCount, inWidth, inHeight, inTextureFormat);
166     else if (immutable)
167         theTexture->setTextureStorage(1, inWidth, inHeight, inTextureFormat);
168     else
169         theTexture->setTextureData(QSSGByteView(), 0, inWidth, inHeight, inTextureFormat);
170 
171     return setupAllocatedTexture(theTexture);
172 }
173 
release(const QSSGRef<QSSGRenderTexture2D> & inBuffer)174 void QSSGResourceManager::release(const QSSGRef<QSSGRenderTexture2D> &inBuffer)
175 {
176 #ifdef _DEBUG
177     auto theFind = std::find(freeTextures.begin(), freeTextures.end(), inBuffer);
178     Q_ASSERT(theFind == freeTextures.end());
179 #endif
180     freeTextures.push_back(inBuffer);
181 }
182 
allocateTextureCube(qint32 inWidth,qint32 inHeight,QSSGRenderTextureFormat inTextureFormat,qint32 inSampleCount)183 QSSGRef<QSSGRenderTextureCube> QSSGResourceManager::allocateTextureCube(qint32 inWidth, qint32 inHeight, QSSGRenderTextureFormat inTextureFormat, qint32 inSampleCount)
184 {
185     bool inMultisample = inSampleCount > 1 && renderContext->supportsMultisampleTextures();
186     for (int idx = 0, end = freeTexCubes.size(); idx < end; ++idx) {
187         auto theTexture = freeTexCubes[idx];
188         QSSGTextureDetails theDetails = theTexture->textureDetails();
189         if (theDetails.width == inWidth && theDetails.height == inHeight && inTextureFormat == theDetails.format
190                 && theTexture->sampleCount() == inSampleCount) {
191             replaceWithLast(freeTexCubes, idx);
192 
193             theTexture->setMinFilter(QSSGRenderTextureMinifyingOp::Linear);
194             theTexture->setMagFilter(QSSGRenderTextureMagnifyingOp::Linear);
195             return theTexture;
196         }
197     }
198 
199     // else resize an existing texture.  This should be fairly quick at the driver level.
200     // note that MSAA textures are not resizable ( in GLES )
201     if (!freeTexCubes.empty() && !inMultisample) {
202         auto theTexture = freeTexCubes.back();
203         freeTexCubes.pop_back();
204 
205         // note we could re-use a former MSAA texture
206         // this causes a entire destroy of the previous texture object
207         theTexture->setTextureData(QSSGByteView(), 0, QSSGRenderTextureCubeFace::CubePosX, inWidth, inHeight, inTextureFormat);
208         theTexture->setTextureData(QSSGByteView(), 0, QSSGRenderTextureCubeFace::CubeNegX, inWidth, inHeight, inTextureFormat);
209         theTexture->setTextureData(QSSGByteView(), 0, QSSGRenderTextureCubeFace::CubePosY, inWidth, inHeight, inTextureFormat);
210         theTexture->setTextureData(QSSGByteView(), 0, QSSGRenderTextureCubeFace::CubeNegY, inWidth, inHeight, inTextureFormat);
211         theTexture->setTextureData(QSSGByteView(), 0, QSSGRenderTextureCubeFace::CubePosZ, inWidth, inHeight, inTextureFormat);
212         theTexture->setTextureData(QSSGByteView(), 0, QSSGRenderTextureCubeFace::CubeNegZ, inWidth, inHeight, inTextureFormat);
213         theTexture->setMinFilter(QSSGRenderTextureMinifyingOp::Linear);
214         theTexture->setMagFilter(QSSGRenderTextureMagnifyingOp::Linear);
215         return theTexture;
216     }
217 
218     // else create a new texture.
219     QSSGRef<QSSGRenderTextureCube> theTexture = nullptr;
220 
221     if (!inMultisample) {
222         theTexture = new QSSGRenderTextureCube(renderContext);
223         theTexture->setTextureData(QSSGByteView(), 0, QSSGRenderTextureCubeFace::CubePosX, inWidth, inHeight, inTextureFormat);
224         theTexture->setTextureData(QSSGByteView(), 0, QSSGRenderTextureCubeFace::CubeNegX, inWidth, inHeight, inTextureFormat);
225         theTexture->setTextureData(QSSGByteView(), 0, QSSGRenderTextureCubeFace::CubePosY, inWidth, inHeight, inTextureFormat);
226         theTexture->setTextureData(QSSGByteView(), 0, QSSGRenderTextureCubeFace::CubeNegY, inWidth, inHeight, inTextureFormat);
227         theTexture->setTextureData(QSSGByteView(), 0, QSSGRenderTextureCubeFace::CubePosZ, inWidth, inHeight, inTextureFormat);
228         theTexture->setTextureData(QSSGByteView(), 0, QSSGRenderTextureCubeFace::CubeNegZ, inWidth, inHeight, inTextureFormat);
229     } else {
230         // Not supported yet
231         return nullptr;
232     }
233 
234     theTexture->setMinFilter(QSSGRenderTextureMinifyingOp::Linear);
235     theTexture->setMagFilter(QSSGRenderTextureMagnifyingOp::Linear);
236     return theTexture;
237 }
238 
release(const QSSGRef<QSSGRenderTextureCube> & inBuffer)239 void QSSGResourceManager::release(const QSSGRef<QSSGRenderTextureCube> &inBuffer)
240 {
241 #ifdef _DEBUG
242     auto theFind = std::find(freeTexCubes.begin(), freeTexCubes.end(), inBuffer);
243     Q_ASSERT(theFind == freeTexCubes.end());
244 #endif
245     freeTexCubes.push_back(inBuffer);
246 }
247 
allocateImage2D(const QSSGRef<QSSGRenderTexture2D> & inTexture,QSSGRenderImageAccessType inAccess)248 QSSGRef<QSSGRenderImage2D> QSSGResourceManager::allocateImage2D(const QSSGRef<QSSGRenderTexture2D> &inTexture,
249                                                                 QSSGRenderImageAccessType inAccess)
250 {
251     if (freeImages.empty() == true) {
252         auto newImage = new QSSGRenderImage2D(renderContext, inTexture, inAccess);
253         if (newImage) {
254             freeImages.push_back(newImage);
255         }
256     }
257 
258     auto retval = freeImages.back();
259     freeImages.pop_back();
260 
261     return retval;
262 }
263 
release(const QSSGRef<QSSGRenderImage2D> & inBuffer)264 void QSSGResourceManager::release(const QSSGRef<QSSGRenderImage2D> &inBuffer)
265 {
266 #ifdef _DEBUG
267     auto theFind = std::find(freeImages.begin(), freeImages.end(), inBuffer);
268     Q_ASSERT(theFind == freeImages.end());
269 #endif
270     freeImages.push_back(inBuffer);
271 }
272 
getRenderContext()273 QSSGRef<QSSGRenderContext> QSSGResourceManager::getRenderContext() { return renderContext; }
274 
destroyFreeSizedResources()275 void QSSGResourceManager::destroyFreeSizedResources()
276 {
277     for (int idx = freeRenderBuffers.size() - 1; idx >= 0; --idx) {
278         auto obj = freeRenderBuffers[idx];
279         replaceWithLast(freeRenderBuffers, idx);
280     }
281     for (int idx = freeTextures.size() - 1; idx >= 0; --idx) {
282         auto obj = freeTextures[idx];
283         replaceWithLast(freeTextures, idx);
284     }
285     for (int idx = freeTexCubes.size() - 1; idx >= 0; --idx) {
286         auto obj = freeTexCubes[idx];
287         replaceWithLast(freeTexCubes, idx);
288     }
289 }
290 
291 QT_END_NAMESPACE
292