• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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