1 | // Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB). |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "imagesubmissioncontext_p.h" |
5 | #include <Qt3DRender/private/shaderimage_p.h> |
6 | #include <Qt3DRender/qshaderimage.h> |
7 | #include <graphicscontext_p.h> |
8 | #include <gltexture_p.h> |
9 | #include <logging_p.h> |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | // ES 3.1+ or GL 4.2+ |
14 | #ifndef GL_READ_ONLY |
15 | #define GL_READ_ONLY 0x88B8 |
16 | #endif |
17 | #ifndef GL_WRITE_ONLY |
18 | #define GL_WRITE_ONLY 0x88B9 |
19 | #endif |
20 | #ifndef GL_READ_WRITE |
21 | #define GL_READ_WRITE 0x88BA |
22 | #endif |
23 | |
24 | namespace Qt3DRender { |
25 | namespace Render { |
26 | namespace OpenGL { |
27 | |
28 | class GraphicsContext; |
29 | class GLTexture; |
30 | |
31 | namespace { |
32 | |
33 | GLenum glAccessEnumForShaderImageAccess(QShaderImage::Access access) |
34 | { |
35 | switch (access) { |
36 | case QShaderImage::ReadOnly: |
37 | return GL_READ_ONLY; |
38 | case QShaderImage::WriteOnly: |
39 | return GL_WRITE_ONLY; |
40 | case QShaderImage::ReadWrite: |
41 | default: |
42 | break; |
43 | } |
44 | return GL_READ_WRITE; |
45 | } |
46 | |
47 | GLenum glImageFormatToGL(QShaderImage::ImageFormat format) |
48 | { |
49 | // Right now we can abuse from the fact that the ImageFormat enum values |
50 | // have been assigned the same value as the GL enum |
51 | return GLenum(format); |
52 | } |
53 | |
54 | GLenum glImageFormatForShaderImageFormat(QShaderImage::ImageFormat format, |
55 | QAbstractTexture::TextureFormat textureFormat) |
56 | { |
57 | Q_ASSERT_X(format != QShaderImage::NoFormat, Q_FUNC_INFO, "Valid image format or Automatic expected" ); |
58 | |
59 | if (format != QShaderImage::Automatic) |
60 | return glImageFormatToGL(format); |
61 | |
62 | // Otherwise try to mind to best texture format |
63 | switch (textureFormat) { |
64 | case QAbstractTexture::R8_UNorm: |
65 | return glImageFormatToGL(format: QShaderImage::R8_UNorm); |
66 | case QAbstractTexture::RG8_UNorm: |
67 | return glImageFormatToGL(format: QShaderImage::RG8_UNorm); |
68 | case QAbstractTexture::RGBA8_UNorm: |
69 | return glImageFormatToGL(format: QShaderImage::RGBA8_UNorm); |
70 | |
71 | case QAbstractTexture::R16_UNorm: |
72 | return glImageFormatToGL(format: QShaderImage::R16_UNorm); |
73 | case QAbstractTexture::RG16_UNorm: |
74 | return glImageFormatToGL(format: QShaderImage::RG16_UNorm); |
75 | case QAbstractTexture::RGBA16_UNorm: |
76 | return glImageFormatToGL(format: QShaderImage::RGBA16_UNorm); |
77 | |
78 | case QAbstractTexture::R8_SNorm: |
79 | return glImageFormatToGL(format: QShaderImage::R8_SNorm); |
80 | case QAbstractTexture::RG8_SNorm: |
81 | return glImageFormatToGL(format: QShaderImage::RG8_SNorm); |
82 | case QAbstractTexture::RGBA8_SNorm: |
83 | return glImageFormatToGL(format: QShaderImage::RGBA8_SNorm); |
84 | |
85 | case QAbstractTexture::R16_SNorm: |
86 | return glImageFormatToGL(format: QShaderImage::R16_SNorm); |
87 | case QAbstractTexture::RG16_SNorm: |
88 | return glImageFormatToGL(format: QShaderImage::RG16_SNorm); |
89 | case QAbstractTexture::RGBA16_SNorm: |
90 | return glImageFormatToGL(format: QShaderImage::RGBA16_SNorm); |
91 | |
92 | case QAbstractTexture::R8U: |
93 | return glImageFormatToGL(format: QShaderImage::R8U); |
94 | case QAbstractTexture::RG8U: |
95 | return glImageFormatToGL(format: QShaderImage::RG8U); |
96 | case QAbstractTexture::RGBA8U: |
97 | return glImageFormatToGL(format: QShaderImage::RGBA8U); |
98 | |
99 | case QAbstractTexture::R16U: |
100 | return glImageFormatToGL(format: QShaderImage::R16U); |
101 | case QAbstractTexture::RG16U: |
102 | return glImageFormatToGL(format: QShaderImage::RG16U); |
103 | case QAbstractTexture::RGBA16U: |
104 | return glImageFormatToGL(format: QShaderImage::RGBA16U); |
105 | |
106 | case QAbstractTexture::R32U: |
107 | return glImageFormatToGL(format: QShaderImage::R32U); |
108 | case QAbstractTexture::RG32U: |
109 | return glImageFormatToGL(format: QShaderImage::RG32U); |
110 | case QAbstractTexture::RGBA32U: |
111 | return glImageFormatToGL(format: QShaderImage::RGBA32U); |
112 | |
113 | case QAbstractTexture::R8I: |
114 | return glImageFormatToGL(format: QShaderImage::R8I); |
115 | case QAbstractTexture::RG8I: |
116 | return glImageFormatToGL(format: QShaderImage::RG8I); |
117 | case QAbstractTexture::RGBA8I: |
118 | return glImageFormatToGL(format: QShaderImage::RGBA8I); |
119 | |
120 | case QAbstractTexture::R16I: |
121 | return glImageFormatToGL(format: QShaderImage::R16I); |
122 | case QAbstractTexture::RG16I: |
123 | return glImageFormatToGL(format: QShaderImage::RG16I); |
124 | case QAbstractTexture::RGBA16I: |
125 | return glImageFormatToGL(format: QShaderImage::RGBA16I); |
126 | |
127 | case QAbstractTexture::R32I: |
128 | return glImageFormatToGL(format: QShaderImage::R32I); |
129 | case QAbstractTexture::RG32I: |
130 | return glImageFormatToGL(format: QShaderImage::RG32I); |
131 | case QAbstractTexture::RGBA32I: |
132 | return glImageFormatToGL(format: QShaderImage::RGBA32I); |
133 | |
134 | case QAbstractTexture::R16F: |
135 | return glImageFormatToGL(format: QShaderImage::R16F); |
136 | case QAbstractTexture::RG16F: |
137 | return glImageFormatToGL(format: QShaderImage::RG16F); |
138 | case QAbstractTexture::RGBA16F: |
139 | return glImageFormatToGL(format: QShaderImage::RGBA16F); |
140 | |
141 | case QAbstractTexture::R32F: |
142 | return glImageFormatToGL(format: QShaderImage::R32F); |
143 | case QAbstractTexture::RG32F: |
144 | return glImageFormatToGL(format: QShaderImage::RG32F); |
145 | case QAbstractTexture::RGBA32F: |
146 | return glImageFormatToGL(format: QShaderImage::RGBA32F); |
147 | |
148 | case QAbstractTexture::RG11B10F: |
149 | return glImageFormatToGL(format: QShaderImage::RG11B10F); |
150 | case QAbstractTexture::RGB10A2: |
151 | return glImageFormatToGL(format: QShaderImage::RGB10A2); |
152 | case QAbstractTexture::RGB10A2U: |
153 | return glImageFormatToGL(format: QShaderImage::RGB10A2U); |
154 | |
155 | default: |
156 | qWarning() << "Cannot map Texture format" << textureFormat << "to a valid Image Format" ; |
157 | Q_UNREACHABLE_RETURN(GL_NONE); |
158 | } |
159 | } |
160 | |
161 | } // anonymous |
162 | |
163 | ImageSubmissionContext::ImageSubmissionContext() |
164 | : m_ctx(nullptr) |
165 | { |
166 | } |
167 | |
168 | void ImageSubmissionContext::initialize(GraphicsContext *context) |
169 | { |
170 | m_ctx = context; |
171 | m_activeImages.resize(new_size: m_ctx->maxImageUnitsCount()); |
172 | } |
173 | |
174 | void ImageSubmissionContext::endDrawing() |
175 | { |
176 | // Reduce score of all active Images |
177 | decayImageScores(); |
178 | } |
179 | |
180 | // Return Image Unit for Image |
181 | // If Image was used previously and recently, it will return the last used unit |
182 | // for that image. Otherwise it will try to return the image unit the least used. |
183 | int ImageSubmissionContext::activateImage(ShaderImage *image, GLTexture *tex) |
184 | { |
185 | const int onUnit = assignUnitForImage(shaderImageId: image->peerId()); |
186 | |
187 | if (onUnit < 0) { |
188 | qWarning() << "Unable to find available image unit" ; |
189 | return -1; |
190 | } |
191 | |
192 | QOpenGLTexture *glTex = tex->getGLTexture(); |
193 | if (glTex == nullptr) { |
194 | qWarning() << "Unable to retrieve valid texture for Image" ; |
195 | return -1; |
196 | } |
197 | |
198 | // Bind Image against Texture and resolve Image Format |
199 | m_ctx->bindImageTexture(imageUnit: onUnit, |
200 | texture: glTex->textureId(), |
201 | mipLevel: image->mipLevel(), |
202 | layered: image->layered(), |
203 | layer: image->layer(), |
204 | access: glAccessEnumForShaderImageAccess(access: image->access()), |
205 | format: glImageFormatForShaderImageFormat(format: image->format(), |
206 | textureFormat: tex->properties().format)); |
207 | |
208 | // Store information about the Texture/Image on ActiveImage for given |
209 | // image unit |
210 | m_activeImages[onUnit].shaderImageId = image->peerId(); |
211 | m_activeImages[onUnit].texture = tex; |
212 | m_activeImages[onUnit].score = 200; |
213 | m_activeImages[onUnit].pinned = true; |
214 | |
215 | return onUnit; |
216 | } |
217 | |
218 | // Unset pinned Active Image and reduce their score |
219 | void ImageSubmissionContext::deactivateImages() |
220 | { |
221 | for (size_t u = 0, m = m_activeImages.size(); u < m; ++u) { |
222 | if (m_activeImages[u].pinned) { |
223 | m_activeImages[u].pinned = false; |
224 | m_activeImages[u].score = qMax(a: m_activeImages[u].score - 1, b: 0); |
225 | return; |
226 | } |
227 | } |
228 | } |
229 | |
230 | // Reduce score of all active images (pinned or not) |
231 | void ImageSubmissionContext::decayImageScores() |
232 | { |
233 | for (size_t u = 0, m = m_activeImages.size(); u < m; ++u) |
234 | m_activeImages[u].score = qMax(a: m_activeImages[u].score - 1, b: 0); |
235 | } |
236 | |
237 | int ImageSubmissionContext::assignUnitForImage(Qt3DCore::QNodeId shaderImageId) |
238 | { |
239 | int lowestScoredUnit = -1; |
240 | int lowestScore = 0xfffffff; |
241 | |
242 | const size_t m = m_activeImages.size(); |
243 | for (size_t u = 0; u < m; ++u) { |
244 | if (m_activeImages[u].shaderImageId == shaderImageId) |
245 | return int(u); |
246 | } |
247 | |
248 | for (size_t u = 0; u < m; ++u) { |
249 | // No image is currently active on the image unit |
250 | // we save the image unit with the texture that has been on there |
251 | // the longest time while not being used |
252 | if (!m_activeImages[u].pinned) { |
253 | const int score = m_activeImages[u].score; |
254 | if (score < lowestScore) { |
255 | lowestScore = score; |
256 | lowestScoredUnit = int(u); |
257 | } |
258 | } |
259 | } |
260 | |
261 | if (lowestScoredUnit == -1) |
262 | qCWarning(Backend) << Q_FUNC_INFO << "No free image units!" ; |
263 | |
264 | return lowestScoredUnit; |
265 | } |
266 | |
267 | } // namespace OpenGL |
268 | } // namespace Render |
269 | } // namespace Qt3DRender |
270 | |
271 | QT_END_NAMESPACE |
272 | |