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
11QT_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
24namespace Qt3DRender {
25namespace Render {
26namespace OpenGL {
27
28class GraphicsContext;
29class GLTexture;
30
31namespace {
32
33GLenum 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
47GLenum 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
54GLenum 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
163ImageSubmissionContext::ImageSubmissionContext()
164 : m_ctx(nullptr)
165{
166}
167
168void ImageSubmissionContext::initialize(GraphicsContext *context)
169{
170 m_ctx = context;
171 m_activeImages.resize(new_size: m_ctx->maxImageUnitsCount());
172}
173
174void 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.
183int 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
219void 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)
231void 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
237int 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
271QT_END_NAMESPACE
272

source code of qt3d/src/plugins/renderers/opengl/graphicshelpers/imagesubmissioncontext.cpp