1 //
2 // Copyright 2022 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // PLSProgramCache.cpp: Implements a cache of native programs used to render load/store operations
8 // for EXT_shader_pixel_local_storage.
9
10 #include "libANGLE/renderer/gl/PLSProgramCache.h"
11
12 #include "libANGLE/Caps.h"
13 #include "libANGLE/PixelLocalStorage.h"
14 #include "libANGLE/renderer/gl/FunctionsGL.h"
15
16 namespace rx
17 {
18 namespace
19 {
CreateVAO(const FunctionsGL * gl)20 GLuint CreateVAO(const FunctionsGL *gl)
21 {
22 GLuint vao;
23 gl->genVertexArrays(1, &vao);
24 return vao;
25 }
26
IsShaderCompiled(const FunctionsGL * gl,GLuint shaderID)27 bool IsShaderCompiled(const FunctionsGL *gl, GLuint shaderID)
28 {
29 GLint isCompiled = 0;
30 gl->getShaderiv(shaderID, GL_COMPILE_STATUS, &isCompiled);
31 return isCompiled != 0;
32 }
33
IsProgramLinked(const FunctionsGL * gl,GLuint programID)34 bool IsProgramLinked(const FunctionsGL *gl, GLuint programID)
35 {
36 GLint isLinked = 0;
37 gl->getProgramiv(programID, GL_LINK_STATUS, &isLinked);
38 return isLinked != 0;
39 }
40
GetPLSFormatKey(GLenum internalformat)41 PLSFormatKey GetPLSFormatKey(GLenum internalformat)
42 {
43 switch (internalformat)
44 {
45 default:
46 UNREACHABLE();
47 [[fallthrough]];
48 case GL_NONE:
49 return PLSFormatKey::INACTIVE;
50 case GL_RGBA8:
51 return PLSFormatKey::RGBA8;
52 case GL_RGBA8I:
53 return PLSFormatKey::RGBA8I;
54 case GL_RGBA8UI:
55 return PLSFormatKey::RGBA8UI;
56 case GL_R32F:
57 return PLSFormatKey::R32F;
58 case GL_R32UI:
59 return PLSFormatKey::R32UI;
60 }
61 }
62
BitRepeat(uint64_t bits,int interval)63 constexpr uint64_t BitRepeat(uint64_t bits, int interval)
64 {
65 return interval >= 64 ? bits : BitRepeat(bits | (bits << interval), interval * 2);
66 }
67
GetPLSQualifier(PLSProgramType type)68 const char *GetPLSQualifier(PLSProgramType type)
69 {
70 switch (type)
71 {
72 case PLSProgramType::Load:
73 return "__pixel_local_outEXT";
74 case PLSProgramType::Store:
75 return "__pixel_local_inEXT";
76 default:
77 UNREACHABLE();
78 return "";
79 }
80 }
81
GetFormatQualifier(PLSFormatKey formatKey)82 const char *GetFormatQualifier(PLSFormatKey formatKey)
83 {
84 switch (formatKey)
85 {
86 case PLSFormatKey::RGBA8:
87 return "rgba8";
88 case PLSFormatKey::RGBA8I:
89 return "rgba8i";
90 case PLSFormatKey::RGBA8UI:
91 return "rgba8ui";
92 case PLSFormatKey::R32F:
93 return "r32f";
94 case PLSFormatKey::R32UI:
95 return "r32ui";
96 default:
97 UNREACHABLE();
98 return "";
99 }
100 }
101
GetFormatPrecision(PLSFormatKey formatKey)102 const char *GetFormatPrecision(PLSFormatKey formatKey)
103 {
104 switch (formatKey)
105 {
106 case PLSFormatKey::RGBA8:
107 case PLSFormatKey::RGBA8I:
108 case PLSFormatKey::RGBA8UI:
109 return "lowp";
110 case PLSFormatKey::R32F:
111 case PLSFormatKey::R32UI:
112 return "highp";
113 default:
114 UNREACHABLE();
115 return "";
116 }
117 }
118
GetFormatRawType(PLSFormatKey formatKey)119 const char *GetFormatRawType(PLSFormatKey formatKey)
120 {
121 switch (formatKey)
122 {
123 case PLSFormatKey::RGBA8:
124 return "vec4";
125 case PLSFormatKey::RGBA8I:
126 return "ivec4";
127 case PLSFormatKey::RGBA8UI:
128 return "uvec4";
129 case PLSFormatKey::R32F:
130 return "float";
131 case PLSFormatKey::R32UI:
132 return "uint";
133 default:
134 UNREACHABLE();
135 return "";
136 }
137 }
138
GetFormatPrefix(PLSFormatKey formatKey)139 const char *GetFormatPrefix(PLSFormatKey formatKey)
140 {
141 switch (formatKey)
142 {
143 case PLSFormatKey::RGBA8:
144 case PLSFormatKey::R32F:
145 return "";
146 case PLSFormatKey::RGBA8I:
147 return "i";
148 case PLSFormatKey::RGBA8UI:
149 case PLSFormatKey::R32UI:
150 return "u";
151 default:
152 UNREACHABLE();
153 return "";
154 }
155 }
156
GetFormatSwizzle(PLSFormatKey formatKey)157 const char *GetFormatSwizzle(PLSFormatKey formatKey)
158 {
159 switch (formatKey)
160 {
161 case PLSFormatKey::RGBA8:
162 case PLSFormatKey::RGBA8I:
163 case PLSFormatKey::RGBA8UI:
164 return "";
165 case PLSFormatKey::R32F:
166 case PLSFormatKey::R32UI:
167 return ".r";
168 default:
169 UNREACHABLE();
170 return "";
171 }
172 }
173
ExpandPLSVar(std::ostringstream & out,int binding,PLSFormatKey formatKey)174 void ExpandPLSVar(std::ostringstream &out, int binding, PLSFormatKey formatKey)
175 {
176 switch (formatKey)
177 {
178 case PLSFormatKey::RGBA8:
179 case PLSFormatKey::RGBA8I:
180 case PLSFormatKey::RGBA8UI:
181 out << "pls._" << binding;
182 break;
183 case PLSFormatKey::R32F:
184 out << "vec4(pls._" << binding << ",0,0,1)";
185 break;
186 case PLSFormatKey::R32UI:
187 out << "uvec4(pls._" << binding << ",0,0,1)";
188 break;
189 default:
190 UNREACHABLE();
191 }
192 }
193 } // namespace
194
PLSProgramCache(const FunctionsGL * gl,const gl::Caps & nativeCaps)195 PLSProgramCache::PLSProgramCache(const FunctionsGL *gl, const gl::Caps &nativeCaps)
196 : mGL(gl),
197 mVertexShaderID(mGL->createShader(GL_VERTEX_SHADER)),
198 mEmptyVAO(CreateVAO(gl)),
199 mEmptyVAOState(nativeCaps.maxVertexAttributes, nativeCaps.maxVertexAttribBindings),
200 mCache(MaximumTotalPrograms)
201 {
202 // Draws a fullscreen quad from a 4-point GL_TRIANGLE_STRIP.
203 constexpr char kLoadPLSVertexShader[] = R"(#version 310 es
204 void main()
205 {
206 gl_Position = vec4(mix(vec2(-1), vec2(1), equal(gl_VertexID & ivec2(1, 2), ivec2(0))), 0, 1);
207 })";
208 const char *loadPLSVertexShaderPtr = kLoadPLSVertexShader;
209 mGL->shaderSource(mVertexShaderID, 1, &loadPLSVertexShaderPtr, nullptr);
210 mGL->compileShader(mVertexShaderID);
211 ASSERT(IsShaderCompiled(mGL, mVertexShaderID));
212 }
213
~PLSProgramCache()214 PLSProgramCache::~PLSProgramCache()
215 {
216 mGL->deleteShader(mVertexShaderID);
217 mGL->deleteVertexArrays(1, &mEmptyVAO);
218 }
219
prependPlane(GLenum internalformat,bool preserved)220 void PLSProgramKeyBuilder::prependPlane(GLenum internalformat, bool preserved)
221 {
222 uint64_t rawFormatKey = static_cast<uint64_t>(GetPLSFormatKey(internalformat));
223 mRawKey = (mRawKey << (PLSProgramKey::BitsPerPlane - 1)) | rawFormatKey;
224 mRawKey = (mRawKey << 1) | static_cast<uint64_t>(preserved);
225 }
226
finish(PLSProgramType type)227 PLSProgramKey PLSProgramKeyBuilder::finish(PLSProgramType type)
228 {
229 return PLSProgramKey((mRawKey << 1) | static_cast<uint64_t>(type));
230 }
231
type() const232 PLSProgramType PLSProgramKey::type() const
233 {
234 return static_cast<PLSProgramType>(mRawKey & 1);
235 }
236
areAnyPreserved() const237 bool PLSProgramKey::areAnyPreserved() const
238 {
239 // The bottom bit of the entire rawKey says whether the program is load or store.
240 // The bottom bit in each individual plane sub-key says whether the plane is preserved.
241 constexpr uint64_t PreserveMask = BitRepeat(1, BitsPerPlane) << 1;
242 return (mRawKey & PreserveMask) != 0;
243 }
244
Iter(const PLSProgramKey & key)245 PLSProgramKey::Iter::Iter(const PLSProgramKey &key)
246 : mPlaneKeys(key.rawKey() >> 1) // The first rawKey bit says if the program is load or store.
247 {
248 skipInactivePlanes();
249 }
250
formatKey() const251 PLSFormatKey PLSProgramKey::Iter::formatKey() const
252 {
253 return static_cast<PLSFormatKey>((mPlaneKeys & SinglePlaneMask) >> 1);
254 }
255
preserved() const256 bool PLSProgramKey::Iter::preserved() const
257 {
258 return mPlaneKeys & 1;
259 }
260
operator !=(const Iter & iter) const261 bool PLSProgramKey::Iter::operator!=(const Iter &iter) const
262 {
263 return mPlaneKeys != iter.mPlaneKeys;
264 }
265
operator ++()266 void PLSProgramKey::Iter::operator++()
267 {
268 ++mBinding;
269 mPlaneKeys >>= BitsPerPlane;
270 skipInactivePlanes();
271 }
272
skipInactivePlanes()273 void PLSProgramKey::Iter::skipInactivePlanes()
274 {
275 // Skip over any planes that are not active. The only effect inactive planes have on shaders is
276 // to offset the next binding index.
277 if (mPlaneKeys != 0 && formatKey() == PLSFormatKey::INACTIVE)
278 {
279 ++*this; // Recurses if there are adjacent inactive planes.
280 }
281 }
282
PLSProgram(const FunctionsGL * gl,GLuint vertexShaderID,PLSProgramKey key)283 PLSProgram::PLSProgram(const FunctionsGL *gl, GLuint vertexShaderID, PLSProgramKey key)
284 : mGL(gl), mKey(key), mProgramID(mGL->createProgram())
285 {
286 std::ostringstream fs;
287 fs << "#version 310 es\n";
288 fs << "#extension GL_EXT_shader_pixel_local_storage : require\n";
289
290 // Emit the global pixel local storage interface block.
291 fs << GetPLSQualifier(mKey.type()) << " PLS{";
292 for (auto [binding, formatKey, preserved] : mKey)
293 {
294 fs << "layout(" << GetFormatQualifier(formatKey) << ")" << GetFormatPrecision(formatKey)
295 << " " << GetFormatRawType(formatKey) << " _" << binding << ";";
296 }
297 fs << "}pls;\n";
298
299 // Emit the sources/destinations of each PLS plane (either images or uniform clear values).
300 for (auto [binding, formatKey, preserved] : mKey)
301 {
302 if (mKey.type() == PLSProgramType::Load)
303 {
304 if (preserved)
305 {
306 // Emit an image to load into the PLS plane.
307 fs << "layout(binding=" << binding << "," << GetFormatQualifier(formatKey)
308 << ")uniform readonly " << GetFormatPrecision(formatKey) << " "
309 << GetFormatPrefix(formatKey) << "image2D i" << binding << ";";
310 }
311 else
312 {
313 // Emit a uniform clear value to initialize the PLS plane with.
314 fs << "uniform " << GetFormatPrecision(formatKey) << " "
315 << GetFormatPrefix(formatKey) << "vec4 c" << binding << ";";
316 }
317 }
318 else
319 {
320 if (preserved)
321 {
322 // Emit an image to dump the PLS plane to.
323 fs << "layout(binding=" << binding << "," << GetFormatQualifier(formatKey)
324 << ")uniform writeonly " << GetFormatPrecision(formatKey) << " "
325 << GetFormatPrefix(formatKey) << "image2D i" << binding << ";";
326 }
327 }
328 }
329
330 fs << "void main(){";
331 if (mKey.areAnyPreserved())
332 {
333 fs << "highp ivec2 pixelCoord=ivec2(floor(gl_FragCoord.xy));";
334 }
335 // Emit the load/store operations for each plane.
336 for (auto [binding, formatKey, preserved] : mKey)
337 {
338 if (mKey.type() == PLSProgramType::Load)
339 {
340 fs << "pls._" << binding << "=";
341 if (preserved)
342 {
343 fs << "imageLoad(i" << binding << ",pixelCoord)";
344 }
345 else
346 {
347 fs << "c" << binding;
348 }
349 fs << GetFormatSwizzle(formatKey) << ";";
350 }
351 else
352 {
353 if (preserved)
354 {
355 fs << "imageStore(i" << binding << ",pixelCoord,";
356 ExpandPLSVar(fs, binding, formatKey);
357 fs << ");";
358 }
359 }
360 }
361 fs << "}";
362
363 // Compile.
364 GLuint fragmentShaderID = mGL->createShader(GL_FRAGMENT_SHADER);
365 std::string str = fs.str();
366 const char *source = str.c_str();
367 mGL->shaderSource(fragmentShaderID, 1, &source, nullptr);
368 mGL->compileShader(fragmentShaderID);
369 ASSERT(IsShaderCompiled(mGL, fragmentShaderID));
370
371 // Link.
372 mGL->attachShader(mProgramID, vertexShaderID);
373 mGL->attachShader(mProgramID, fragmentShaderID);
374 mGL->linkProgram(mProgramID);
375 ASSERT(IsProgramLinked(mGL, mProgramID));
376
377 mGL->deleteShader(fragmentShaderID);
378
379 // Get the locations of uniform clear values.
380 if (mKey.type() == PLSProgramType::Load)
381 {
382 for (auto [binding, formatKey, preserved] : mKey)
383 {
384 if (!preserved)
385 {
386 std::ostringstream name;
387 name << "c" << binding;
388 mClearValueUniformLocations[binding] = {
389 mGL->getUniformLocation(mProgramID, name.str().c_str())};
390 ASSERT(mClearValueUniformLocations[binding] >= 0);
391 }
392 }
393 }
394 }
395
~PLSProgram()396 PLSProgram::~PLSProgram()
397 {
398 mGL->deleteProgram(mProgramID);
399 }
400
setClearValues(const gl::PixelLocalStoragePlane planes[],const GLenum loadops[]) const401 void PLSProgram::setClearValues(const gl::PixelLocalStoragePlane planes[],
402 const GLenum loadops[]) const
403 {
404 ASSERT(mKey.type() == PLSProgramType::Load);
405
406 // Updates uniforms representing clear values on the current program.
407 class ClearUniformCommands : public gl::PixelLocalStoragePlane::ClearCommands
408 {
409 public:
410 ClearUniformCommands(const FunctionsGL *gl, const GLint *clearValueUniformLocations)
411 : mGL(gl), mClearValueUniformLocations(clearValueUniformLocations)
412 {}
413
414 void clearfv(int binding, const GLfloat value[]) const override
415 {
416 mGL->uniform4fv(mClearValueUniformLocations[binding], 1, value);
417 }
418
419 void cleariv(int binding, const GLint value[]) const override
420 {
421 mGL->uniform4iv(mClearValueUniformLocations[binding], 1, value);
422 }
423
424 void clearuiv(int binding, const GLuint value[]) const override
425 {
426 mGL->uniform4uiv(mClearValueUniformLocations[binding], 1, value);
427 }
428
429 private:
430 const FunctionsGL *const mGL;
431 const GLint *const mClearValueUniformLocations;
432 };
433
434 ClearUniformCommands clearUniformCommands(mGL, mClearValueUniformLocations.data());
435 for (auto [binding, formatKey, preserved] : mKey)
436 {
437 if (!preserved)
438 {
439 planes[binding].issueClearCommand(&clearUniformCommands, binding, loadops[binding]);
440 }
441 }
442 }
443
getProgram(PLSProgramKey key)444 const PLSProgram *PLSProgramCache::getProgram(PLSProgramKey key)
445 {
446 const std::unique_ptr<PLSProgram> *programPtr;
447 if (!mCache.get(key.rawKey(), &programPtr))
448 {
449 auto program = std::make_unique<PLSProgram>(mGL, mVertexShaderID, key);
450 programPtr = mCache.put(key.rawKey(), std::move(program), 1);
451 }
452 ASSERT(programPtr != nullptr);
453 return programPtr->get();
454 }
455 } // namespace rx
456