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