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