• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "node_context_pool_manager_gles.h"
17 
18 #include <algorithm>
19 
20 #include <render/namespace.h>
21 
22 #include "device/gpu_resource_manager.h"
23 #include "gles/device_gles.h"
24 #include "gles/gl_functions.h"
25 #include "gles/gpu_image_gles.h"
26 #include "nodecontext/render_command_list.h" // RenderPassBeginInfo...
27 #include "util/log.h"
28 
29 using namespace BASE_NS;
30 
31 RENDER_BEGIN_NAMESPACE()
32 namespace {
33 constexpr const bool VERBOSE_LOGGING = false;
34 constexpr const bool HASH_LAYOUTS = false;
35 constexpr const uint32_t EMPTY_ATTACHMENT = ~0u;
36 
37 struct BindImage {
38     uint32_t layer;
39     uint32_t mipLevel;
40     const GpuImageGLES* image;
41 };
42 
43 struct FboHash {
44     uint64_t hash;
45     GLuint fbo;
46 };
47 
HighestBit(uint32_t value)48 uint32_t HighestBit(uint32_t value)
49 {
50     uint32_t count = 0;
51     while (value) {
52         ++count;
53         value >>= 1U;
54     }
55     return count;
56 }
57 
UpdateBindImages(const RenderCommandBeginRenderPass & beginRenderPass,array_view<BindImage> images,GpuResourceManager & gpuResourceMgr_)58 void UpdateBindImages(const RenderCommandBeginRenderPass& beginRenderPass, array_view<BindImage> images,
59     GpuResourceManager& gpuResourceMgr_)
60 {
61     const auto& renderPassDesc = beginRenderPass.renderPassDesc;
62     for (uint32_t idx = 0; idx < renderPassDesc.attachmentCount; ++idx) {
63         images[idx].layer = renderPassDesc.attachments[idx].layer;
64         images[idx].mipLevel = renderPassDesc.attachments[idx].mipLevel;
65         images[idx].image = gpuResourceMgr_.GetImage<GpuImageGLES>(renderPassDesc.attachmentHandles[idx]);
66     }
67 }
68 
HashRPD(const RenderCommandBeginRenderPass & beginRenderPass,GpuResourceManager & gpuResourceMgr)69 uint64_t HashRPD(const RenderCommandBeginRenderPass& beginRenderPass, GpuResourceManager& gpuResourceMgr)
70 {
71     const auto& renderPassDesc = beginRenderPass.renderPassDesc;
72     uint64_t rpHash = 0;
73     // hash engine gpu handle
74     {
75         for (uint32_t idx = 0; idx < renderPassDesc.attachmentCount; ++idx) {
76             HashCombine(rpHash, renderPassDesc.attachments[idx].layer);
77             HashCombine(rpHash, renderPassDesc.attachments[idx].mipLevel);
78             // generation counters and hashing with handles is not enough
79             // the reason is that e.g. shallow handles can point to to different GPU handles
80             // and have counter of zero in their index
81             // this can lead with handle re-use to situations where the gen counter is zero
82             // NOTE: we hash with our own gpuHandle and gl image (if gl image id would be re-used)
83             const RenderHandle clientHandle = renderPassDesc.attachmentHandles[idx];
84             const EngineResourceHandle gpuHandle = gpuResourceMgr.GetGpuHandle(clientHandle);
85             uint64_t imageId = clientHandle.id;
86             if (const GpuImageGLES* image = gpuResourceMgr.GetImage<GpuImageGLES>(clientHandle); image) {
87                 imageId = (uint64_t)(image->GetPlatformData().image);
88             }
89             HashCombine(rpHash, gpuHandle.id, imageId);
90         }
91     }
92 
93     // hash input and output layouts (ignored since, they do not contribute in GL/GLES at all, they are a artifact
94     // of vulkan)
95     if constexpr (HASH_LAYOUTS) {
96         const RenderPassImageLayouts& renderPassImageLayouts = beginRenderPass.imageLayouts;
97         for (uint32_t idx = 0; idx < renderPassDesc.attachmentCount; ++idx) {
98             HashCombine(rpHash, renderPassImageLayouts.attachmentInitialLayouts[idx],
99                 renderPassImageLayouts.attachmentFinalLayouts[idx]);
100         }
101     }
102 
103     // hash subpasses
104     PLUGIN_ASSERT(renderPassDesc.subpassCount <= beginRenderPass.subpasses.size());
105     for (const RenderPassSubpassDesc& subpass : beginRenderPass.subpasses) {
106         HashRange(
107             rpHash, subpass.inputAttachmentIndices, subpass.inputAttachmentIndices + subpass.inputAttachmentCount);
108         HashRange(
109             rpHash, subpass.colorAttachmentIndices, subpass.colorAttachmentIndices + subpass.colorAttachmentCount);
110         if (subpass.depthAttachmentCount) {
111             HashCombine(rpHash, (uint64_t)subpass.depthAttachmentIndex);
112         }
113         HashCombine(rpHash, (uint64_t)subpass.viewMask);
114     }
115     return rpHash;
116 }
117 
VerifyFBO()118 bool VerifyFBO()
119 {
120     GLenum status;
121     status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
122     if (status != GL_FRAMEBUFFER_COMPLETE) {
123         // failed!
124 #if (RENDER_VALIDATION_ENABLED == 1)
125         switch (status) {
126             case GL_FRAMEBUFFER_UNDEFINED:
127                 // is returned if target is the default framebuffer, but the default framebuffer does not exist.
128                 PLUGIN_LOG_E("Framebuffer undefined");
129                 break;
130             case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
131                 // is returned if any of the framebuffer attachment points are framebuffer incomplete.
132                 PLUGIN_LOG_E("Framebuffer incomplete attachment");
133                 break;
134             case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
135                 // is returned if the framebuffer does not haveat least one image attached to it.
136                 PLUGIN_LOG_E("Framebuffer incomplete missing attachment");
137                 break;
138             case GL_FRAMEBUFFER_UNSUPPORTED:
139                 // is returned if depth and stencil attachments, if present, are not the same renderbuffer, or
140                 // if the combination of internal formats of the attached images violates an
141                 // implementation-dependent set of restrictions.
142                 PLUGIN_LOG_E("Framebuffer unsupported");
143                 break;
144             case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
145                 // is returned if the value of GL_RENDERBUFFER_SAMPLES  is not the same for all attached
146                 // renderbuffers or, if the attached images are a mix of renderbuffers and textures, the value
147                 // of GL_RENDERBUFFER_SAMPLES is not zero.
148                 PLUGIN_LOG_E("Framebuffer incomplete multisample");
149                 break;
150             case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
151                 // is returned if any framebuffer attachment is layered, and any populated attachment is not
152                 // layered, or if all populated color attachments are not from textures of the same target.
153                 PLUGIN_LOG_E("Framebuffer incomplete layer targets");
154                 break;
155             case GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR:
156                 PLUGIN_LOG_E("Framebuffer incomplete view targets");
157                 break;
158             default: {
159                 PLUGIN_LOG_E("Framebuffer other error: %x", status);
160                 break;
161             }
162         }
163 #endif
164         return false;
165     }
166     return true;
167 }
168 
HasStencil(const GpuImageGLES * image)169 bool HasStencil(const GpuImageGLES* image)
170 {
171     const auto& dplat = static_cast<const GpuImagePlatformDataGL&>(image->GetPlatformData());
172     return (dplat.format == GL_STENCIL_INDEX) || (dplat.format == GL_DEPTH_STENCIL);
173 }
174 
HasDepth(const GpuImageGLES * image)175 bool HasDepth(const GpuImageGLES* image)
176 {
177     const auto& dplat = static_cast<const GpuImagePlatformDataGL&>(image->GetPlatformData());
178     return (dplat.format == GL_DEPTH_COMPONENT) || (dplat.format == GL_DEPTH_STENCIL);
179 }
180 
181 #if RENDER_GL_FLIP_Y_SWAPCHAIN == 0
IsDefaultAttachment(array_view<const BindImage> images,const RenderPassSubpassDesc & sb)182 bool IsDefaultAttachment(array_view<const BindImage> images, const RenderPassSubpassDesc& sb)
183 {
184     // Valid backbuffer configurations are.
185     // 1. color only
186     // 2. color + depth (stencil).
187     // It is not allowed to mix custom render targets and backbuffer!
188     if (sb.colorAttachmentCount == 1) {
189         // okay, looks good. one color...
190         if (const auto* color = images[sb.colorAttachmentIndices[0]].image) {
191             const auto& plat = static_cast<const GpuImagePlatformDataGL&>(color->GetPlatformData());
192             if ((plat.image == 0) &&                // not texture
193                 (plat.renderBuffer == 0))           // not renderbuffer
194             {                                       // Colorattachment is backbuffer
195                 if (sb.depthAttachmentCount == 1) { // and has one depth.
196                     const auto depth = images[sb.depthAttachmentIndex].image;
197                     const auto& dPlat = static_cast<const GpuImagePlatformDataGL&>(depth->GetPlatformData());
198                     // NOTE: CORE_DEFAULT_BACKBUFFER_DEPTH is not used legacy way anymore
199                     if ((dPlat.image == 0) && (dPlat.renderBuffer == 0)) { // depth attachment is backbuffer depth.
200                         return true;
201                     } else {
202                         // Invalid configuration. (this should be caught earlier already)
203 #if (RENDER_VALIDATION_ENABLED == 1)
204                         PLUGIN_LOG_ONCE_I("backbuffer_depth_gles_mixing" + to_string(plat.image),
205                             "RENDER_VALIDATION: Depth target dropped due to using swapchain buffer (depth)");
206 #endif
207                         return true;
208                     }
209                 } else {
210                     return true;
211                 }
212             }
213         }
214     }
215     return false;
216 }
217 
IsDefaultResolve(array_view<const BindImage> images,const RenderPassSubpassDesc & sb)218 bool IsDefaultResolve(array_view<const BindImage> images, const RenderPassSubpassDesc& sb)
219 {
220     if (sb.resolveAttachmentCount == 1 && sb.depthResolveAttachmentCount == 0) {
221         // looks good, one color
222         if (sb.resolveAttachmentIndices[0U] < static_cast<uint32_t>(images.size())) {
223             if (const GpuImageGLES* color = images[sb.resolveAttachmentIndices[0]].image; color) {
224                 const auto& plat = static_cast<const GpuImagePlatformDataGL&>(color->GetPlatformData());
225                 if ((plat.image == 0) && (plat.renderBuffer == 0)) {
226                     return true;
227                 }
228             }
229         }
230     }
231     if (sb.depthResolveAttachmentCount == 1) {
232         if (sb.depthResolveAttachmentIndex < static_cast<uint32_t>(images.size())) {
233             // looks good, one depth
234             if (const GpuImageGLES* depth = images[sb.depthResolveAttachmentIndex].image; depth) {
235                 const auto& plat = static_cast<const GpuImagePlatformDataGL&>(depth->GetPlatformData());
236                 if ((plat.image == 0) && (plat.renderBuffer == 0)) {
237                     return true;
238                 }
239             }
240         }
241     }
242     return false;
243 }
244 #endif
245 
DeleteFbos(DeviceGLES & device,LowlevelFramebufferGL & ref)246 void DeleteFbos(DeviceGLES& device, LowlevelFramebufferGL& ref)
247 {
248     for (uint32_t i = 0; i < ref.fbos.size(); i++) {
249         GLuint f, r;
250         f = ref.fbos[i].fbo;
251         r = ref.fbos[i].resolve;
252         if (f != 0) {
253             device.DeleteFrameBuffer(f);
254         }
255         if (r != 0) {
256             device.DeleteFrameBuffer(r);
257         }
258         // the same fbos can be used multiple render passes, so clean those references too.
259         for (auto& fbo : ref.fbos) {
260             if (f == fbo.fbo) {
261                 fbo.fbo = 0;
262             }
263             if (r == fbo.resolve) {
264                 fbo.resolve = 0;
265             }
266         }
267     }
268     ref.fbos.clear();
269 }
270 
BindToFbo(GLenum attachType,const BindImage & image,uint32_t & width,uint32_t & height,uint32_t views,bool isStarted)271 void BindToFbo(
272     GLenum attachType, const BindImage& image, uint32_t& width, uint32_t& height, uint32_t views, bool isStarted)
273 {
274     const auto& plat = static_cast<const GpuImagePlatformDataGL&>(image.image->GetPlatformData());
275     const GpuImageDesc& desc = image.image->GetDesc();
276     if (isStarted) {
277 #if (RENDER_VALIDATION_ENABLED == 1)
278         if (width != desc.width) {
279             PLUGIN_LOG_W("Attachment is not the same size (%d) as other attachments (%u)", desc.width, width);
280         }
281         if (height != desc.height) {
282             PLUGIN_LOG_W("Attachment is not the same size (%d) as other attachments (%u)", desc.height, height);
283         }
284 #endif
285         width = Math::min(width, desc.width);
286         height = Math::min(height, desc.height);
287     } else {
288         width = desc.width;
289         height = desc.height;
290     }
291     const bool isSrc = (desc.usageFlags & CORE_IMAGE_USAGE_TRANSFER_SRC_BIT);
292     const bool isDst = (desc.usageFlags & CORE_IMAGE_USAGE_TRANSFER_DST_BIT);
293     const bool isSample = (desc.usageFlags & CORE_IMAGE_USAGE_SAMPLED_BIT);
294     const bool isStorage = (desc.usageFlags & CORE_IMAGE_USAGE_STORAGE_BIT);
295     // could check for bool isColor = (desc.usageFlags & CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
296     // could check for isDepth = (desc.usageFlags & CORE_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
297     // could check for bool isTrans = (desc.usageFlags & CORE_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT);
298     const bool isInput = (desc.usageFlags & CORE_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
299     PLUGIN_UNUSED(isSrc);
300     PLUGIN_UNUSED(isDst);
301     PLUGIN_UNUSED(isSample);
302     PLUGIN_UNUSED(isStorage);
303     PLUGIN_UNUSED(isInput);
304     if (plat.renderBuffer) {
305         PLUGIN_ASSERT((!isSrc) && (!isDst) && (!isSample) && (!isStorage) && (!isInput));
306         glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachType, GL_RENDERBUFFER, plat.renderBuffer);
307     } else {
308         if ((plat.type == GL_TEXTURE_2D_ARRAY) || (plat.type == GL_TEXTURE_2D_MULTISAMPLE_ARRAY)) {
309             if (views) {
310                 glFramebufferTextureMultiviewOVR(
311                     GL_FRAMEBUFFER, attachType, plat.image, (GLint)image.mipLevel, (GLint)image.layer, (GLsizei)views);
312             } else {
313                 glFramebufferTextureLayer(
314                     GL_FRAMEBUFFER, attachType, plat.image, (GLint)image.mipLevel, (GLint)image.layer);
315             }
316         } else {
317             glFramebufferTexture2D(GL_FRAMEBUFFER, attachType, plat.type, plat.image, (GLint)image.mipLevel);
318         }
319     }
320 }
321 
BindToFboMultisampled(GLenum attachType,const BindImage & image,const BindImage & resolveImage,uint32_t & width,uint32_t & height,uint32_t views,bool isStarted,bool multisampledRenderToTexture)322 void BindToFboMultisampled(GLenum attachType, const BindImage& image, const BindImage& resolveImage, uint32_t& width,
323     uint32_t& height, uint32_t views, bool isStarted, bool multisampledRenderToTexture)
324 {
325     const auto& plat = static_cast<const GpuImagePlatformDataGL&>(resolveImage.image->GetPlatformData());
326     const GpuImageDesc& desc = image.image->GetDesc();
327     if (isStarted) {
328 #if (RENDER_VALIDATION_ENABLED == 1)
329         if (width != desc.width) {
330             PLUGIN_LOG_W("Attachment is not the same size (%d) as other attachments (%u)", desc.width, width);
331         }
332         if (height != desc.height) {
333             PLUGIN_LOG_W("Attachment is not the same size (%d) as other attachments (%u)", desc.height, height);
334         }
335 #endif
336         width = Math::min(width, desc.width);
337         height = Math::min(height, desc.height);
338     } else {
339         width = desc.width;
340         height = desc.height;
341     }
342     const bool isSrc = (desc.usageFlags & CORE_IMAGE_USAGE_TRANSFER_SRC_BIT);
343     const bool isDst = (desc.usageFlags & CORE_IMAGE_USAGE_TRANSFER_DST_BIT);
344     const bool isSample = (desc.usageFlags & CORE_IMAGE_USAGE_SAMPLED_BIT);
345     const bool isStorage = (desc.usageFlags & CORE_IMAGE_USAGE_STORAGE_BIT);
346     // could check for bool isColor = (desc.usageFlags & CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
347     // could check for isDepth = (desc.usageFlags & CORE_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
348     const bool isTrans = (desc.usageFlags & CORE_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT);
349     const bool isInput = (desc.usageFlags & CORE_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
350     PLUGIN_UNUSED(isSrc);
351     PLUGIN_UNUSED(isDst);
352     PLUGIN_UNUSED(isSample);
353     PLUGIN_UNUSED(isStorage);
354     PLUGIN_UNUSED(isTrans);
355     PLUGIN_UNUSED(isInput);
356     if (plat.renderBuffer) {
357         PLUGIN_ASSERT((!isSrc) && (!isDst) && (!isSample) && (!isStorage) && (!isInput));
358         glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachType, GL_RENDERBUFFER, plat.renderBuffer);
359     } else if ((plat.type == GL_TEXTURE_2D_ARRAY) || (plat.type == GL_TEXTURE_2D_MULTISAMPLE_ARRAY)) {
360 #if RENDER_HAS_GLES_BACKEND
361         if (views && multisampledRenderToTexture && isTrans &&
362             ((plat.type == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) || (desc.sampleCountFlags & ~CORE_SAMPLE_COUNT_1_BIT))) {
363             const auto samples = (desc.sampleCountFlags & CORE_SAMPLE_COUNT_8_BIT)
364                                      ? 8
365                                      : ((desc.sampleCountFlags & CORE_SAMPLE_COUNT_4_BIT) ? 4 : 2);
366             glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, attachType, plat.image, GLint(image.mipLevel),
367                 GLint(samples), GLint(image.layer), GLsizei(views));
368         } else {
369 #endif
370             if (views) {
371                 glFramebufferTextureMultiviewOVR(
372                     GL_FRAMEBUFFER, attachType, plat.image, GLint(image.mipLevel), GLint(image.layer), GLsizei(views));
373             } else {
374                 glFramebufferTextureLayer(
375                     GL_FRAMEBUFFER, attachType, plat.image, GLint(image.mipLevel), GLint(image.layer));
376             }
377 #if RENDER_HAS_GLES_BACKEND
378         }
379     } else if (multisampledRenderToTexture && isTrans &&
380                ((plat.type == GL_TEXTURE_2D_MULTISAMPLE) || (desc.sampleCountFlags & ~CORE_SAMPLE_COUNT_1_BIT))) {
381         const auto samples = (desc.sampleCountFlags & CORE_SAMPLE_COUNT_8_BIT)
382                                  ? 8
383                                  : ((desc.sampleCountFlags & CORE_SAMPLE_COUNT_4_BIT) ? 4 : 2);
384         glFramebufferTexture2DMultisampleEXT(
385             GL_FRAMEBUFFER, attachType, plat.type, plat.image, (GLint)image.mipLevel, samples);
386 #endif
387     } else {
388         glFramebufferTexture2D(GL_FRAMEBUFFER, attachType, plat.type, plat.image, (GLint)image.mipLevel);
389     }
390 }
391 
HashAttachments(const RenderPassSubpassDesc & sb)392 uint64_t HashAttachments(const RenderPassSubpassDesc& sb)
393 {
394     // generate hash for attachments.
395     uint64_t subHash = 0;
396     HashRange(subHash, sb.colorAttachmentIndices, sb.colorAttachmentIndices + sb.colorAttachmentCount);
397     if (sb.depthAttachmentCount) {
398         HashCombine(subHash, (uint64_t)sb.depthAttachmentIndex);
399     }
400     return subHash;
401 }
402 
BindType(const GpuImageGLES * image)403 GLenum BindType(const GpuImageGLES* image)
404 {
405     GLenum bindType = GL_NONE;
406     const bool depth = HasDepth(image);
407     const bool stencil = HasStencil(image);
408     if (depth && stencil) {
409         bindType = GL_DEPTH_STENCIL_ATTACHMENT;
410     } else if (depth) {
411         bindType = GL_DEPTH_ATTACHMENT;
412     } else if (stencil) {
413         bindType = GL_STENCIL_ATTACHMENT;
414     }
415     return bindType;
416 }
417 
GenerateSubPassFBO(DeviceGLES & device,LowlevelFramebufferGL & framebuffer,const RenderPassSubpassDesc & sb,const array_view<const BindImage> images,const size_t resolveAttachmentCount,const array_view<const uint32_t> imageMap,bool multisampledRenderToTexture)418 uint32_t GenerateSubPassFBO(DeviceGLES& device, LowlevelFramebufferGL& framebuffer, const RenderPassSubpassDesc& sb,
419     const array_view<const BindImage> images, const size_t resolveAttachmentCount,
420     const array_view<const uint32_t> imageMap, bool multisampledRenderToTexture)
421 {
422     // generate fbo for subpass (depth/color).
423     GLuint fbo;
424     glGenFramebuffers(1, &fbo);
425 #if (RENDER_DEBUG_GPU_RESOURCE_IDS == 1)
426     PLUGIN_LOG_D("fbo id >: %u", fbo);
427 #endif
428     device.BindFrameBuffer(fbo);
429     GLenum drawBuffers[PipelineStateConstants::MAX_COLOR_ATTACHMENT_COUNT] = { GL_NONE };
430     GLenum colorAttachmentCount = 0;
431     const auto views = HighestBit(sb.viewMask);
432     for (uint32_t idx = 0; idx < sb.colorAttachmentCount; ++idx) {
433         const uint32_t ci = sb.colorAttachmentIndices[idx];
434         const uint32_t original = (ci < imageMap.size()) ? imageMap[ci] : EMPTY_ATTACHMENT;
435         if (images[ci].image) {
436             drawBuffers[idx] = GL_COLOR_ATTACHMENT0 + colorAttachmentCount;
437             if (original == EMPTY_ATTACHMENT) {
438                 BindToFbo(drawBuffers[idx], images[ci], framebuffer.width, framebuffer.height, views,
439                     (colorAttachmentCount) || (resolveAttachmentCount));
440             } else {
441                 BindToFboMultisampled(drawBuffers[idx], images[original], images[ci], framebuffer.width,
442                     framebuffer.height, views, (colorAttachmentCount) || (resolveAttachmentCount),
443                     multisampledRenderToTexture);
444             }
445             ++colorAttachmentCount;
446         } else {
447 #if (RENDER_VALIDATION_ENABLED == 1)
448             PLUGIN_LOG_E("RENDER_VALIDATION: no image for color attachment %u %u", idx, ci);
449 #endif
450             drawBuffers[idx] = GL_NONE;
451         }
452     }
453     glDrawBuffers((GLsizei)sb.colorAttachmentCount, drawBuffers);
454     if (sb.depthAttachmentCount == 1) {
455         const uint32_t di = sb.depthAttachmentIndex;
456         const auto* image = images[di].image;
457         uint32_t original = (di < imageMap.size()) ? imageMap[di] : EMPTY_ATTACHMENT;
458         if (original == EMPTY_ATTACHMENT) {
459             original = di;
460         }
461         if (image) {
462             const GLenum bindType = BindType(image);
463             PLUGIN_ASSERT_MSG(bindType != GL_NONE, "Depth attachment has no stencil or depth");
464             BindToFboMultisampled(bindType, images[original], images[di], framebuffer.width, framebuffer.height, views,
465                 (colorAttachmentCount) || (resolveAttachmentCount), multisampledRenderToTexture);
466         } else {
467             PLUGIN_LOG_E("no image for depth attachment");
468         }
469     }
470     if (!VerifyFBO()) {
471 #if (RENDER_VALIDATION_ENABLED == 1)
472         PLUGIN_LOG_E("RENDER_VALIDATION: Failed to create subpass FBO size [%u %u] [color:%u depth:%u resolve:%u]",
473             framebuffer.width, framebuffer.height, sb.colorAttachmentCount, sb.depthAttachmentCount,
474             sb.resolveAttachmentCount);
475 #endif
476         device.BindFrameBuffer(0U);
477         glDeleteFramebuffers(1, &fbo);
478         fbo = 0U;
479     } else if constexpr (VERBOSE_LOGGING) {
480         PLUGIN_LOG_V("Created subpass FBO size [%u %u] [color:%u depth:%u resolve:%u]", framebuffer.width,
481             framebuffer.height, sb.colorAttachmentCount, sb.depthAttachmentCount, sb.resolveAttachmentCount);
482     }
483     return fbo;
484 }
485 
486 struct ResolvePair {
487     uint32_t resolveAttachmentCount;
488     uint32_t resolveFbo;
489 };
490 
GenerateResolveFBO(DeviceGLES & device,LowlevelFramebufferGL & framebuffer,const RenderPassSubpassDesc & sb,array_view<const BindImage> images)491 ResolvePair GenerateResolveFBO(DeviceGLES& device, LowlevelFramebufferGL& framebuffer, const RenderPassSubpassDesc& sb,
492     array_view<const BindImage> images)
493 {
494     // generate fbos for resolve attachments if needed.
495     if ((sb.resolveAttachmentCount == 0) && (sb.depthResolveAttachmentCount == 0)) {
496         return { 0, 0 };
497     }
498 #if RENDER_GL_FLIP_Y_SWAPCHAIN == 0
499     // currently resolving to backbuffer AND other attachments at the same time is not possible.
500     if (IsDefaultResolve(images, sb)) {
501         // resolving from custom render target to default fbo.
502         const auto* color = images[sb.colorAttachmentIndices[0]].image;
503         if (color) {
504             const auto& desc = color->GetDesc();
505             framebuffer.width = desc.width;
506             framebuffer.height = desc.height;
507         }
508         return { 1, 0 };
509     }
510 #endif
511     // all subpasses with resolve will get a special resolve fbo.. (expecting that no more than one
512     // subpass resolves to a single attachment. if more than one subpass resolves to a single
513     // attachment we have extra fbos.)
514     ResolvePair rp { 0, 0 };
515     glGenFramebuffers(1, &rp.resolveFbo);
516 #if (RENDER_DEBUG_GPU_RESOURCE_IDS == 1)
517     PLUGIN_LOG_D("fbo id >: %u", rp.resolveFbo);
518 #endif
519     const auto views = HighestBit(sb.viewMask);
520     device.BindFrameBuffer(rp.resolveFbo);
521     GLenum drawBuffers[PipelineStateConstants::MAX_COLOR_ATTACHMENT_COUNT] = { GL_NONE };
522     for (uint32_t idx = 0; idx < sb.resolveAttachmentCount; ++idx) {
523         const uint32_t ci = sb.resolveAttachmentIndices[idx];
524         const auto* image = images[ci].image;
525         if (image) {
526             drawBuffers[idx] = GL_COLOR_ATTACHMENT0 + rp.resolveAttachmentCount;
527             BindToFbo(drawBuffers[idx], images[ci], framebuffer.width, framebuffer.height, views,
528                 (rp.resolveAttachmentCount > 0));
529             ++rp.resolveAttachmentCount;
530         } else {
531             PLUGIN_LOG_E("no image for resolve attachment %u %u", idx, ci);
532             drawBuffers[idx] = GL_NONE;
533         }
534     }
535     glDrawBuffers((GLsizei)sb.resolveAttachmentCount, drawBuffers);
536     for (uint32_t idx = 0; idx < sb.depthResolveAttachmentCount; ++idx) {
537         const uint32_t ci = sb.depthResolveAttachmentIndex;
538         const auto* image = images[ci].image;
539         if (image) {
540             BindToFbo(BindType(image), images[ci], framebuffer.width, framebuffer.height, views,
541                 (rp.resolveAttachmentCount > 0));
542         } else {
543             PLUGIN_LOG_E("no image for depth resolve attachment %u %u", idx, ci);
544         }
545     }
546     return rp;
547 }
548 
ProcessSubPass(DeviceGLES & device,LowlevelFramebufferGL & framebuffer,vector<FboHash> & fboMap,array_view<const BindImage> images,const array_view<const uint32_t> imageMap,const RenderPassSubpassDesc & sb,bool multisampledRenderToTexture)549 LowlevelFramebufferGL::SubPassPair ProcessSubPass(DeviceGLES& device, LowlevelFramebufferGL& framebuffer,
550     vector<FboHash>& fboMap, array_view<const BindImage> images, const array_view<const uint32_t> imageMap,
551     const RenderPassSubpassDesc& sb, bool multisampledRenderToTexture)
552 {
553 #if RENDER_GL_FLIP_Y_SWAPCHAIN == 0
554     if (IsDefaultAttachment(images, sb)) {
555         // This subpass uses backbuffer!
556         const auto* color = images[sb.colorAttachmentIndices[0]].image;
557         if (color) {
558             const auto& desc = color->GetDesc();
559             framebuffer.width = desc.width;
560             framebuffer.height = desc.height;
561         }
562         // NOTE: it is technically possible to resolve from backbuffer to a custom render target.
563         // but we do not support it now.
564         if (sb.resolveAttachmentCount != 0) {
565             PLUGIN_LOG_E("No resolving from default framebuffer");
566         }
567         return { 0, 0 };
568     }
569 #endif
570     // This subpass uses custom render targets.
571     PLUGIN_ASSERT((sb.colorAttachmentCount + sb.depthAttachmentCount) <
572                   (PipelineStateConstants::MAX_COLOR_ATTACHMENT_COUNT + 1)); // +1 for depth
573     uint32_t fbo = 0;
574     const auto resolveResult = GenerateResolveFBO(device, framebuffer, sb, images);
575     const uint64_t subHash = HashAttachments(sb);
576     if (const auto it = std::find_if(
577             fboMap.begin(), fboMap.end(), [subHash](const FboHash& fboHash) { return fboHash.hash == subHash; });
578         it != fboMap.end()) {
579         // matching fbo created already, re-use
580         fbo = it->fbo;
581     } else {
582         fbo = GenerateSubPassFBO(device, framebuffer, sb, images, resolveResult.resolveAttachmentCount, imageMap,
583             multisampledRenderToTexture);
584         fboMap.push_back({ subHash, fbo });
585     }
586     return { fbo, resolveResult.resolveFbo };
587 }
588 
589 #if RENDER_HAS_GLES_BACKEND
MapColorAttachments(array_view<RenderPassSubpassDesc>::iterator begin,array_view<RenderPassSubpassDesc>::iterator pos,array_view<const BindImage> images,array_view<uint32_t> imageMap,array_view<RenderPassDesc::AttachmentDesc> attachments)590 void MapColorAttachments([[maybe_unused]] array_view<RenderPassSubpassDesc>::iterator begin,
591     array_view<RenderPassSubpassDesc>::iterator pos, array_view<const BindImage> images, array_view<uint32_t> imageMap,
592     array_view<RenderPassDesc::AttachmentDesc> attachments)
593 {
594     auto resolveAttachmentIndices = array_view(pos->resolveAttachmentIndices, pos->resolveAttachmentCount);
595     const auto colorAttachmentIndices = array_view(pos->colorAttachmentIndices, pos->colorAttachmentCount);
596     for (auto i = 0U; i < pos->resolveAttachmentCount; ++i) {
597         const auto color = colorAttachmentIndices[i];
598         // if the attachment can be used as an input attachment we can't render directly to the resolve attachment.
599         if (images[color].image &&
600             (images[color].image->GetDesc().usageFlags & ImageUsageFlagBits::CORE_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)) {
601 #if (RENDER_VALIDATION_ENABLED == 1)
602             const auto subpassIdx = static_cast<uint32_t>(pos - begin);
603             const auto unique = to_string(subpassIdx) + " " + to_string(color);
604             PLUGIN_LOG_ONCE_W(unique,
605                 "Subpass %u attachment %u might be used as input attachment, cannot render directly to "
606                 "texture.",
607                 subpassIdx, color);
608 #endif
609             continue;
610         }
611         const auto resolve = exchange(resolveAttachmentIndices[i], EMPTY_ATTACHMENT);
612         // map the original attachment as the resolve
613         imageMap[color] = resolve;
614 
615         // update the resolve attachment's loadOp and clearValue to match the original attachment.
616         attachments[resolve].loadOp = attachments[color].loadOp;
617         attachments[resolve].clearValue = attachments[color].clearValue;
618     }
619 }
620 
MapDepthAttachments(array_view<RenderPassSubpassDesc>::iterator begin,array_view<RenderPassSubpassDesc>::iterator pos,array_view<const BindImage> images,array_view<uint32_t> imageMap,array_view<RenderPassDesc::AttachmentDesc> attachments)621 void MapDepthAttachments([[maybe_unused]] array_view<RenderPassSubpassDesc>::iterator begin,
622     array_view<RenderPassSubpassDesc>::iterator pos, array_view<const BindImage> images, array_view<uint32_t> imageMap,
623     array_view<RenderPassDesc::AttachmentDesc> attachments)
624 {
625     if ((pos->depthResolveAttachmentCount > 0) && (pos->depthResolveModeFlags || pos->stencilResolveModeFlags)) {
626         const auto depth = pos->depthAttachmentIndex;
627         // if the attachment can be used as an input attachment we can't render directly to the resolve attachment.
628         if (images[depth].image &&
629             (images[depth].image->GetDesc().usageFlags & ImageUsageFlagBits::CORE_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)) {
630 #if (RENDER_VALIDATION_ENABLED == 1)
631             const auto subpassIdx = static_cast<uint32_t>(pos - begin);
632             const auto unique = to_string(subpassIdx) + " " + to_string(depth);
633             PLUGIN_LOG_ONCE_W(unique,
634                 "Subpass %u attachment %u might be used as input attachment, cannot render directly to "
635                 "texture.",
636                 subpassIdx, depth);
637 #endif
638             return;
639         }
640         const auto resolve = pos->depthResolveAttachmentIndex;
641         // map the original attachment as the resolve
642         imageMap[depth] = resolve;
643 
644         // update the resolve attachment's loadOp and clearValue to match the original attachment.
645         attachments[resolve].loadOp = attachments[depth].loadOp;
646         attachments[resolve].clearValue = attachments[depth].clearValue;
647     }
648 }
649 
UpdateSubpassAttachments(array_view<RenderPassSubpassDesc>::iterator begin,array_view<RenderPassSubpassDesc>::iterator end,array_view<uint32_t> imageMap)650 void UpdateSubpassAttachments(array_view<RenderPassSubpassDesc>::iterator begin,
651     array_view<RenderPassSubpassDesc>::iterator end, array_view<uint32_t> imageMap)
652 {
653     // update all the attachment indices in the subpasses according to the mapping.
654     for (auto i = begin; i != end; ++i) {
655         for (auto ci = 0U; ci < i->colorAttachmentCount; ++ci) {
656             const auto oldColor = i->colorAttachmentIndices[ci];
657             if (const auto newColor = imageMap[oldColor]; newColor != EMPTY_ATTACHMENT) {
658                 i->colorAttachmentIndices[ci] = newColor;
659             }
660         }
661         // check what is the last index with a valid resolve attachment
662         auto resolveCount = i->resolveAttachmentCount;
663         for (; resolveCount > 0u; --resolveCount) {
664             if (i->resolveAttachmentIndices[resolveCount - 1] != EMPTY_ATTACHMENT) {
665                 break;
666             }
667         }
668         i->resolveAttachmentCount = resolveCount;
669 
670         const auto oldDepth = i->depthAttachmentIndex;
671         if (oldDepth != EMPTY_ATTACHMENT) {
672             if (const auto newDepth = imageMap[oldDepth]; newDepth != EMPTY_ATTACHMENT) {
673                 i->depthAttachmentIndex = newDepth;
674                 i->depthResolveAttachmentCount = 0;
675             }
676         }
677     }
678 }
679 #endif
680 } // namespace
681 
NodeContextPoolManagerGLES(Device & device,GpuResourceManager & gpuResourceManager)682 NodeContextPoolManagerGLES::NodeContextPoolManagerGLES(Device& device, GpuResourceManager& gpuResourceManager)
683     : NodeContextPoolManager(), device_ { (DeviceGLES&)device }, gpuResourceMgr_ { gpuResourceManager }
684 {
685     bufferingCount_ = device_.GetCommandBufferingCount();
686 #if RENDER_HAS_GLES_BACKEND
687     if (device_.GetBackendType() == DeviceBackendType::OPENGLES) {
688         multisampledRenderToTexture_ = device_.HasExtension("GL_EXT_multisampled_render_to_texture2");
689         multiViewMultisampledRenderToTexture_ = device_.HasExtension("GL_OVR_multiview_multisampled_render_to_texture");
690     }
691 #endif
692     multiView_ = device_.HasExtension("GL_OVR_multiview2");
693 }
694 
~NodeContextPoolManagerGLES()695 NodeContextPoolManagerGLES::~NodeContextPoolManagerGLES()
696 {
697     if (!framebufferCache_.framebuffers.empty()) {
698         PLUGIN_ASSERT(device_.IsActive());
699         for (auto& ref : framebufferCache_.framebuffers) {
700             DeleteFbos(device_, ref);
701         }
702     }
703 }
704 
BeginFrame()705 void NodeContextPoolManagerGLES::BeginFrame()
706 {
707 #if (RENDER_VALIDATION_ENABLED == 1)
708     frameIndexFront_ = device_.GetFrameCount();
709 #endif
710 }
711 
BeginBackendFrame()712 void NodeContextPoolManagerGLES::BeginBackendFrame()
713 {
714     const uint64_t frameCount = device_.GetFrameCount();
715 
716 #if (RENDER_VALIDATION_ENABLED == 1)
717     PLUGIN_ASSERT(frameIndexBack_ != frameCount); // prevent multiple calls per frame
718     frameIndexBack_ = frameCount;
719     PLUGIN_ASSERT(frameIndexFront_ == frameIndexBack_);
720 #endif
721 
722     // not used
723     bufferingIndex_ = 0;
724 
725     const auto maxAge = 2;
726     const auto minAge = device_.GetCommandBufferingCount() + maxAge;
727     const auto ageLimit = (frameCount < minAge) ? 0 : (frameCount - minAge);
728     const size_t limit = framebufferCache_.frameBufferFrameUseIndex.size();
729     for (size_t index = 0; index < limit; ++index) {
730         auto const useIndex = framebufferCache_.frameBufferFrameUseIndex[index];
731         auto& ref = framebufferCache_.framebuffers[index];
732         if (useIndex < ageLimit && (!ref.fbos.empty())) {
733             DeleteFbos(device_, ref);
734             auto const pos = std::find_if(framebufferCache_.renderPassHashToIndex.begin(),
735                 framebufferCache_.renderPassHashToIndex.end(),
736                 [index](auto const& hashToIndex) { return hashToIndex.second == index; });
737             if (pos != framebufferCache_.renderPassHashToIndex.end()) {
738                 framebufferCache_.renderPassHashToIndex.erase(pos);
739             }
740         }
741     }
742 }
743 
GetFramebufferHandle(const RenderCommandBeginRenderPass & beginRenderPass)744 EngineResourceHandle NodeContextPoolManagerGLES::GetFramebufferHandle(
745     const RenderCommandBeginRenderPass& beginRenderPass)
746 {
747     PLUGIN_ASSERT(device_.IsActive());
748     const uint64_t rpHash = HashRPD(beginRenderPass, gpuResourceMgr_);
749     if (const auto iter = framebufferCache_.renderPassHashToIndex.find(rpHash);
750         iter != framebufferCache_.renderPassHashToIndex.cend()) {
751         PLUGIN_ASSERT(iter->second < framebufferCache_.framebuffers.size());
752         // store frame index for usage
753         framebufferCache_.frameBufferFrameUseIndex[iter->second] = device_.GetFrameCount();
754         return RenderHandleUtil::CreateEngineResourceHandle(RenderHandleType::UNDEFINED, iter->second, 0);
755     }
756 
757     BindImage images[PipelineStateConstants::MAX_RENDER_PASS_ATTACHMENT_COUNT];
758     UpdateBindImages(beginRenderPass, images, gpuResourceMgr_);
759 
760     // Create fbo for each subpass
761     LowlevelFramebufferGL fb;
762 
763     const auto& rpd = beginRenderPass.renderPassDesc;
764     fb.fbos.resize(rpd.subpassCount);
765     if constexpr (VERBOSE_LOGGING) {
766         PLUGIN_LOG_V("Creating framebuffer with %u subpasses", rpd.subpassCount);
767     }
768     // NOTE: we currently expect that resolve, color and depth attachments are regular 2d textures (or
769     // renderbuffers)
770     vector<FboHash> fboMap;
771     std::transform(std::begin(beginRenderPass.subpasses), std::begin(beginRenderPass.subpasses) + rpd.subpassCount,
772         fb.fbos.data(),
773         [this, &fb, &fboMap, images = array_view<const BindImage>(images),
774             imageMap = array_view<const uint32_t>(imageMap_)](const RenderPassSubpassDesc& subpass) {
775             return ProcessSubPass(device_, fb, fboMap, images, imageMap, subpass,
776                 subpass.viewMask ? multiViewMultisampledRenderToTexture_ : multisampledRenderToTexture_);
777         });
778     if constexpr (VERBOSE_LOGGING) {
779         PLUGIN_LOG_V("Created framebuffer with %u subpasses at size [%u %u]", rpd.subpassCount, fb.width, fb.height);
780     }
781     if (!fb.width || !fb.height) {
782         return {};
783     }
784     uint32_t arrayIndex = 0;
785     if (auto const pos = std::find_if(framebufferCache_.framebuffers.begin(), framebufferCache_.framebuffers.end(),
786         [](auto const& framebuffer) { return framebuffer.fbos.empty(); });
787         pos != framebufferCache_.framebuffers.end()) {
788         arrayIndex = (uint32_t)std::distance(framebufferCache_.framebuffers.begin(), pos);
789         *pos = move(fb);
790         framebufferCache_.frameBufferFrameUseIndex[arrayIndex] = device_.GetFrameCount();
791     } else {
792         framebufferCache_.framebuffers.push_back(move(fb));
793         framebufferCache_.frameBufferFrameUseIndex.push_back(device_.GetFrameCount());
794         arrayIndex = (uint32_t)framebufferCache_.framebuffers.size() - 1;
795     }
796     framebufferCache_.renderPassHashToIndex[rpHash] = arrayIndex;
797     return RenderHandleUtil::CreateEngineResourceHandle(RenderHandleType::UNDEFINED, arrayIndex, 0);
798 }
799 
GetFramebuffer(const EngineResourceHandle handle) const800 const LowlevelFramebufferGL* NodeContextPoolManagerGLES::GetFramebuffer(const EngineResourceHandle handle) const
801 {
802     if (RenderHandleUtil::IsValid(handle)) {
803         if (const uint32_t index = RenderHandleUtil::GetIndexPart(handle);
804             (index < framebufferCache_.framebuffers.size())) {
805             return &framebufferCache_.framebuffers[index];
806         }
807     }
808     return nullptr;
809 }
810 
FilterRenderPass(RenderCommandBeginRenderPass & beginRenderPass)811 void NodeContextPoolManagerGLES::FilterRenderPass(RenderCommandBeginRenderPass& beginRenderPass)
812 {
813 #if RENDER_HAS_GLES_BACKEND
814     if (!multisampledRenderToTexture_) {
815         return;
816     }
817     if (!multiViewMultisampledRenderToTexture_ &&
818         std::any_of(beginRenderPass.subpasses.cbegin(), beginRenderPass.subpasses.cend(),
819             [](const RenderPassSubpassDesc& subpass) { return subpass.viewMask != 0; })) {
820         return;
821     }
822     imageMap_.clear();
823     imageMap_.insert(
824         imageMap_.end(), static_cast<size_t>(beginRenderPass.renderPassDesc.attachmentCount), EMPTY_ATTACHMENT);
825     auto begin = beginRenderPass.subpasses.begin();
826     auto end = beginRenderPass.subpasses.end();
827     auto pos = std::find_if(begin, end, [](const RenderPassSubpassDesc& subpass) {
828         return (subpass.resolveAttachmentCount > 0) ||
829                ((subpass.depthResolveAttachmentCount > 0) &&
830                    (subpass.depthResolveModeFlags || subpass.stencilResolveModeFlags));
831     });
832     if (pos != end) {
833         BindImage images[PipelineStateConstants::MAX_RENDER_PASS_ATTACHMENT_COUNT];
834         UpdateBindImages(beginRenderPass, images, gpuResourceMgr_);
835         if (!IsDefaultResolve(images, *pos)) {
836             // build a mapping which attachments can be skipped and render directly to the resolve
837             MapColorAttachments(begin, pos, images, imageMap_, beginRenderPass.renderPassDesc.attachments);
838             if (device_.IsDepthResolveSupported()) {
839                 MapDepthAttachments(begin, pos, images, imageMap_, beginRenderPass.renderPassDesc.attachments);
840             }
841             UpdateSubpassAttachments(begin, pos + 1, imageMap_);
842 
843             // flip the mapping from attachment -> resolve to resolve -> attachment. this will be used when selecting
844             // what images are bound as attachments at the begining of the renderpass.
845             vector<uint32_t> map(imageMap_.size(), EMPTY_ATTACHMENT);
846             for (uint32_t color = 0U, last = static_cast<uint32_t>(imageMap_.size()); color < last; ++color) {
847                 const auto resolve = imageMap_[color];
848                 if (resolve != EMPTY_ATTACHMENT) {
849                     map[resolve] = color;
850                 }
851             }
852             imageMap_ = map;
853         }
854     }
855 #endif
856 }
857 RENDER_END_NAMESPACE()
858