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