1 // Copyright 2017 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include "Common/Assert.h"
6 #include "Common/CommonTypes.h"
7 #include "Common/MsgHandler.h"
8 
9 #include "VideoBackends/OGL/OGLTexture.h"
10 #include "VideoBackends/OGL/SamplerCache.h"
11 
12 namespace OGL
13 {
14 namespace
15 {
GetGLInternalFormatForTextureFormat(AbstractTextureFormat format,bool storage)16 GLenum GetGLInternalFormatForTextureFormat(AbstractTextureFormat format, bool storage)
17 {
18   switch (format)
19   {
20   case AbstractTextureFormat::DXT1:
21     return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
22   case AbstractTextureFormat::DXT3:
23     return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
24   case AbstractTextureFormat::DXT5:
25     return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
26   case AbstractTextureFormat::BPTC:
27     return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
28   case AbstractTextureFormat::RGBA8:
29     return storage ? GL_RGBA8 : GL_RGBA;
30   case AbstractTextureFormat::BGRA8:
31     return storage ? GL_RGBA8 : GL_BGRA;
32   case AbstractTextureFormat::R16:
33     return GL_R16;
34   case AbstractTextureFormat::R32F:
35     return GL_R32F;
36   case AbstractTextureFormat::D16:
37     return GL_DEPTH_COMPONENT16;
38   case AbstractTextureFormat::D24_S8:
39     return GL_DEPTH24_STENCIL8;
40   case AbstractTextureFormat::D32F:
41     return GL_DEPTH_COMPONENT32F;
42   case AbstractTextureFormat::D32F_S8:
43     return GL_DEPTH32F_STENCIL8;
44   default:
45     PanicAlert("Unhandled texture format.");
46     return storage ? GL_RGBA8 : GL_RGBA;
47   }
48 }
49 
GetGLFormatForTextureFormat(AbstractTextureFormat format)50 GLenum GetGLFormatForTextureFormat(AbstractTextureFormat format)
51 {
52   switch (format)
53   {
54   case AbstractTextureFormat::RGBA8:
55     return GL_RGBA;
56   case AbstractTextureFormat::BGRA8:
57     return GL_BGRA;
58   case AbstractTextureFormat::R16:
59   case AbstractTextureFormat::R32F:
60     return GL_RED;
61   case AbstractTextureFormat::D16:
62   case AbstractTextureFormat::D32F:
63     return GL_DEPTH_COMPONENT;
64   case AbstractTextureFormat::D24_S8:
65   case AbstractTextureFormat::D32F_S8:
66     return GL_DEPTH_STENCIL;
67   // Compressed texture formats don't use this parameter.
68   default:
69     return GL_UNSIGNED_BYTE;
70   }
71 }
72 
GetGLTypeForTextureFormat(AbstractTextureFormat format)73 GLenum GetGLTypeForTextureFormat(AbstractTextureFormat format)
74 {
75   switch (format)
76   {
77   case AbstractTextureFormat::RGBA8:
78   case AbstractTextureFormat::BGRA8:
79     return GL_UNSIGNED_BYTE;
80   case AbstractTextureFormat::R16:
81     return GL_UNSIGNED_SHORT;
82   case AbstractTextureFormat::R32F:
83     return GL_FLOAT;
84   case AbstractTextureFormat::D16:
85     return GL_UNSIGNED_SHORT;
86   case AbstractTextureFormat::D24_S8:
87     return GL_UNSIGNED_INT_24_8;
88   case AbstractTextureFormat::D32F:
89     return GL_FLOAT;
90   case AbstractTextureFormat::D32F_S8:
91     return GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
92   // Compressed texture formats don't use this parameter.
93   default:
94     return GL_UNSIGNED_BYTE;
95   }
96 }
97 
UsePersistentStagingBuffers()98 bool UsePersistentStagingBuffers()
99 {
100   // We require ARB_buffer_storage to create the persistent mapped buffer,
101   // ARB_shader_image_load_store for glMemoryBarrier, and ARB_sync to ensure
102   // the GPU has finished the copy before reading the buffer from the CPU.
103   return g_ogl_config.bSupportsGLBufferStorage && g_ogl_config.bSupportsImageLoadStore &&
104          g_ogl_config.bSupportsGLSync;
105 }
106 }  // Anonymous namespace
107 
OGLTexture(const TextureConfig & tex_config)108 OGLTexture::OGLTexture(const TextureConfig& tex_config) : AbstractTexture(tex_config)
109 {
110   DEBUG_ASSERT_MSG(VIDEO, !tex_config.IsMultisampled() || tex_config.levels == 1,
111                    "OpenGL does not support multisampled textures with mip levels");
112 
113   const GLenum target = GetGLTarget();
114   glGenTextures(1, &m_texId);
115   glActiveTexture(GL_MUTABLE_TEXTURE_INDEX);
116   glBindTexture(target, m_texId);
117 
118   glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, m_config.levels - 1);
119 
120   GLenum gl_internal_format = GetGLInternalFormatForTextureFormat(m_config.format, true);
121   if (tex_config.IsMultisampled())
122   {
123     if (g_ogl_config.bSupportsTextureStorage)
124       glTexStorage3DMultisample(target, tex_config.samples, gl_internal_format, m_config.width,
125                                 m_config.height, m_config.layers, GL_FALSE);
126     else
127       glTexImage3DMultisample(target, tex_config.samples, gl_internal_format, m_config.width,
128                               m_config.height, m_config.layers, GL_FALSE);
129   }
130   else if (g_ogl_config.bSupportsTextureStorage)
131   {
132     glTexStorage3D(target, m_config.levels, gl_internal_format, m_config.width, m_config.height,
133                    m_config.layers);
134   }
135 
136   if (m_config.IsRenderTarget())
137   {
138     // We can't render to compressed formats.
139     ASSERT(!IsCompressedFormat(m_config.format));
140     if (!g_ogl_config.bSupportsTextureStorage && !tex_config.IsMultisampled())
141     {
142       for (u32 level = 0; level < m_config.levels; level++)
143       {
144         glTexImage3D(target, level, gl_internal_format, std::max(m_config.width >> level, 1u),
145                      std::max(m_config.height >> level, 1u), m_config.layers, 0,
146                      GetGLFormatForTextureFormat(m_config.format),
147                      GetGLTypeForTextureFormat(m_config.format), nullptr);
148       }
149     }
150   }
151 }
152 
~OGLTexture()153 OGLTexture::~OGLTexture()
154 {
155   Renderer::GetInstance()->UnbindTexture(this);
156   glDeleteTextures(1, &m_texId);
157 }
158 
CopyRectangleFromTexture(const AbstractTexture * src,const MathUtil::Rectangle<int> & src_rect,u32 src_layer,u32 src_level,const MathUtil::Rectangle<int> & dst_rect,u32 dst_layer,u32 dst_level)159 void OGLTexture::CopyRectangleFromTexture(const AbstractTexture* src,
160                                           const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
161                                           u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
162                                           u32 dst_layer, u32 dst_level)
163 {
164   const OGLTexture* src_gltex = static_cast<const OGLTexture*>(src);
165   ASSERT(src_rect.GetWidth() == dst_rect.GetWidth() &&
166          src_rect.GetHeight() == dst_rect.GetHeight());
167   if (g_ogl_config.bSupportsCopySubImage)
168   {
169     glCopyImageSubData(src_gltex->m_texId, src_gltex->GetGLTarget(), src_level, src_rect.left,
170                        src_rect.top, src_layer, m_texId, GetGLTarget(), dst_level, dst_rect.left,
171                        dst_rect.top, dst_layer, dst_rect.GetWidth(), dst_rect.GetHeight(), 1);
172   }
173   else
174   {
175     BlitFramebuffer(const_cast<OGLTexture*>(src_gltex), src_rect, src_layer, src_level, dst_rect,
176                     dst_layer, dst_level);
177   }
178 }
179 
BlitFramebuffer(OGLTexture * srcentry,const MathUtil::Rectangle<int> & src_rect,u32 src_layer,u32 src_level,const MathUtil::Rectangle<int> & dst_rect,u32 dst_layer,u32 dst_level)180 void OGLTexture::BlitFramebuffer(OGLTexture* srcentry, const MathUtil::Rectangle<int>& src_rect,
181                                  u32 src_layer, u32 src_level,
182                                  const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
183                                  u32 dst_level)
184 {
185   Renderer::GetInstance()->BindSharedReadFramebuffer();
186   glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, srcentry->m_texId, src_level,
187                             src_layer);
188   Renderer::GetInstance()->BindSharedDrawFramebuffer();
189   glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texId, dst_level,
190                             dst_layer);
191 
192   // glBlitFramebuffer is still affected by the scissor test, which is enabled by default.
193   glDisable(GL_SCISSOR_TEST);
194 
195   glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, dst_rect.left,
196                     dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, GL_NEAREST);
197 
198   // The default state for the scissor test is enabled. We don't need to do a full state
199   // restore, as the framebuffer and scissor test are the only things we changed.
200   glEnable(GL_SCISSOR_TEST);
201   Renderer::GetInstance()->RestoreFramebufferBinding();
202 }
203 
ResolveFromTexture(const AbstractTexture * src,const MathUtil::Rectangle<int> & rect,u32 layer,u32 level)204 void OGLTexture::ResolveFromTexture(const AbstractTexture* src,
205                                     const MathUtil::Rectangle<int>& rect, u32 layer, u32 level)
206 {
207   const OGLTexture* srcentry = static_cast<const OGLTexture*>(src);
208   DEBUG_ASSERT(m_config.samples > 1 && m_config.width == srcentry->m_config.width &&
209                m_config.height == srcentry->m_config.height && m_config.samples == 1);
210   DEBUG_ASSERT(rect.left + rect.GetWidth() <= static_cast<int>(srcentry->m_config.width) &&
211                rect.top + rect.GetHeight() <= static_cast<int>(srcentry->m_config.height));
212   BlitFramebuffer(const_cast<OGLTexture*>(srcentry), rect, layer, level, rect, layer, level);
213 }
214 
Load(u32 level,u32 width,u32 height,u32 row_length,const u8 * buffer,size_t buffer_size)215 void OGLTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
216                       size_t buffer_size)
217 {
218   if (level >= m_config.levels)
219     PanicAlert("Texture only has %d levels, can't update level %d", m_config.levels, level);
220   if (width != std::max(1u, m_config.width >> level) ||
221       height != std::max(1u, m_config.height >> level))
222     PanicAlert("size of level %d must be %dx%d, but %dx%d requested", level,
223                std::max(1u, m_config.width >> level), std::max(1u, m_config.height >> level), width,
224                height);
225 
226   const GLenum target = GetGLTarget();
227   glActiveTexture(GL_MUTABLE_TEXTURE_INDEX);
228   glBindTexture(target, m_texId);
229 
230   if (row_length != width)
231     glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length);
232 
233   GLenum gl_internal_format = GetGLInternalFormatForTextureFormat(m_config.format, false);
234   if (IsCompressedFormat(m_config.format))
235   {
236     if (g_ogl_config.bSupportsTextureStorage)
237     {
238       glCompressedTexSubImage3D(target, level, 0, 0, 0, width, height, 1, gl_internal_format,
239                                 static_cast<GLsizei>(buffer_size), buffer);
240     }
241     else
242     {
243       glCompressedTexImage3D(target, level, gl_internal_format, width, height, 1, 0,
244                              static_cast<GLsizei>(buffer_size), buffer);
245     }
246   }
247   else
248   {
249     GLenum gl_format = GetGLFormatForTextureFormat(m_config.format);
250     GLenum gl_type = GetGLTypeForTextureFormat(m_config.format);
251     if (g_ogl_config.bSupportsTextureStorage)
252     {
253       glTexSubImage3D(target, level, 0, 0, 0, width, height, 1, gl_format, gl_type, buffer);
254     }
255     else
256     {
257       glTexImage3D(target, level, gl_internal_format, width, height, 1, 0, gl_format, gl_type,
258                    buffer);
259     }
260   }
261 
262   if (row_length != width)
263     glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
264 }
265 
GetGLFormatForImageTexture() const266 GLenum OGLTexture::GetGLFormatForImageTexture() const
267 {
268   return GetGLInternalFormatForTextureFormat(m_config.format, true);
269 }
270 
OGLStagingTexture(StagingTextureType type,const TextureConfig & config,GLenum target,GLuint buffer_name,size_t buffer_size,char * map_ptr,size_t map_stride)271 OGLStagingTexture::OGLStagingTexture(StagingTextureType type, const TextureConfig& config,
272                                      GLenum target, GLuint buffer_name, size_t buffer_size,
273                                      char* map_ptr, size_t map_stride)
274     : AbstractStagingTexture(type, config), m_target(target), m_buffer_name(buffer_name),
275       m_buffer_size(buffer_size)
276 {
277   m_map_pointer = map_ptr;
278   m_map_stride = map_stride;
279 }
280 
~OGLStagingTexture()281 OGLStagingTexture::~OGLStagingTexture()
282 {
283   if (m_fence != 0)
284     glDeleteSync(m_fence);
285   if (m_map_pointer)
286   {
287     glBindBuffer(GL_PIXEL_PACK_BUFFER, m_buffer_name);
288     glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
289     glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
290   }
291   if (m_buffer_name != 0)
292     glDeleteBuffers(1, &m_buffer_name);
293 }
294 
Create(StagingTextureType type,const TextureConfig & config)295 std::unique_ptr<OGLStagingTexture> OGLStagingTexture::Create(StagingTextureType type,
296                                                              const TextureConfig& config)
297 {
298   size_t stride = config.GetStride();
299   size_t buffer_size = stride * config.height;
300   GLenum target =
301       type == StagingTextureType::Readback ? GL_PIXEL_PACK_BUFFER : GL_PIXEL_UNPACK_BUFFER;
302   GLuint buffer;
303   glGenBuffers(1, &buffer);
304   glBindBuffer(target, buffer);
305 
306   // Prefer using buffer_storage where possible. This allows us to skip the map/unmap steps.
307   char* buffer_ptr;
308   if (UsePersistentStagingBuffers())
309   {
310     GLenum buffer_flags;
311     GLenum map_flags;
312     if (type == StagingTextureType::Readback)
313     {
314       buffer_flags = GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT;
315       map_flags = GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT;
316     }
317     else if (type == StagingTextureType::Upload)
318     {
319       buffer_flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT;
320       map_flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
321     }
322     else
323     {
324       buffer_flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT;
325       map_flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT;
326     }
327 
328     glBufferStorage(target, buffer_size, nullptr, buffer_flags);
329     buffer_ptr = reinterpret_cast<char*>(glMapBufferRange(target, 0, buffer_size, map_flags));
330     ASSERT(buffer_ptr != nullptr);
331   }
332   else
333   {
334     // Otherwise, fallback to mapping the buffer each time.
335     glBufferData(target, buffer_size, nullptr,
336                  type == StagingTextureType::Readback ? GL_STREAM_READ : GL_STREAM_DRAW);
337     buffer_ptr = nullptr;
338   }
339   glBindBuffer(target, 0);
340 
341   return std::unique_ptr<OGLStagingTexture>(
342       new OGLStagingTexture(type, config, target, buffer, buffer_size, buffer_ptr, stride));
343 }
344 
CopyFromTexture(const AbstractTexture * src,const MathUtil::Rectangle<int> & src_rect,u32 src_layer,u32 src_level,const MathUtil::Rectangle<int> & dst_rect)345 void OGLStagingTexture::CopyFromTexture(const AbstractTexture* src,
346                                         const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
347                                         u32 src_level, const MathUtil::Rectangle<int>& dst_rect)
348 {
349   ASSERT(m_type == StagingTextureType::Readback || m_type == StagingTextureType::Mutable);
350   ASSERT(src_rect.GetWidth() == dst_rect.GetWidth() &&
351          src_rect.GetHeight() == dst_rect.GetHeight());
352   ASSERT(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= src->GetConfig().width &&
353          src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= src->GetConfig().height);
354   ASSERT(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= m_config.width &&
355          dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= m_config.height);
356 
357   // Unmap the buffer before writing when not using persistent mappings.
358   if (!UsePersistentStagingBuffers())
359     OGLStagingTexture::Unmap();
360 
361   // Copy from the texture object to the staging buffer.
362   glBindBuffer(GL_PIXEL_PACK_BUFFER, m_buffer_name);
363   glPixelStorei(GL_PACK_ROW_LENGTH, m_config.width);
364 
365   const OGLTexture* gltex = static_cast<const OGLTexture*>(src);
366   const size_t dst_offset = dst_rect.top * m_config.GetStride() + dst_rect.left * m_texel_size;
367 
368   // Prefer glGetTextureSubImage(), when available.
369   if (g_ogl_config.bSupportsTextureSubImage)
370   {
371     glGetTextureSubImage(
372         gltex->GetGLTextureId(), src_level, src_rect.left, src_rect.top, src_layer,
373         src_rect.GetWidth(), src_rect.GetHeight(), 1, GetGLFormatForTextureFormat(src->GetFormat()),
374         GetGLTypeForTextureFormat(src->GetFormat()),
375         static_cast<GLsizei>(m_buffer_size - dst_offset), reinterpret_cast<void*>(dst_offset));
376   }
377   else
378   {
379     // Mutate the shared framebuffer.
380     Renderer::GetInstance()->BindSharedReadFramebuffer();
381     if (AbstractTexture::IsDepthFormat(gltex->GetFormat()))
382     {
383       glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 0, 0, 0);
384       glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, gltex->GetGLTextureId(),
385                                 src_level, src_layer);
386     }
387     else
388     {
389       glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, gltex->GetGLTextureId(),
390                                 src_level, src_layer);
391       glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, 0, 0, 0);
392     }
393     glReadPixels(src_rect.left, src_rect.top, src_rect.GetWidth(), src_rect.GetHeight(),
394                  GetGLFormatForTextureFormat(src->GetFormat()),
395                  GetGLTypeForTextureFormat(src->GetFormat()), reinterpret_cast<void*>(dst_offset));
396     Renderer::GetInstance()->RestoreFramebufferBinding();
397   }
398 
399   glPixelStorei(GL_PACK_ROW_LENGTH, 0);
400   glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
401 
402   // If we support buffer storage, create a fence for synchronization.
403   if (UsePersistentStagingBuffers())
404   {
405     if (m_fence != 0)
406       glDeleteSync(m_fence);
407 
408     glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT);
409     m_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
410     glFlush();
411   }
412 
413   m_needs_flush = true;
414 }
415 
CopyToTexture(const MathUtil::Rectangle<int> & src_rect,AbstractTexture * dst,const MathUtil::Rectangle<int> & dst_rect,u32 dst_layer,u32 dst_level)416 void OGLStagingTexture::CopyToTexture(const MathUtil::Rectangle<int>& src_rect,
417                                       AbstractTexture* dst,
418                                       const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
419                                       u32 dst_level)
420 {
421   ASSERT(m_type == StagingTextureType::Upload || m_type == StagingTextureType::Mutable);
422   ASSERT(src_rect.GetWidth() == dst_rect.GetWidth() &&
423          src_rect.GetHeight() == dst_rect.GetHeight());
424   ASSERT(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= m_config.width &&
425          src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= m_config.height);
426   ASSERT(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= dst->GetConfig().width &&
427          dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= dst->GetConfig().height);
428 
429   const OGLTexture* gltex = static_cast<const OGLTexture*>(dst);
430   const size_t src_offset = src_rect.top * m_config.GetStride() + src_rect.left * m_texel_size;
431   const size_t copy_size = src_rect.GetHeight() * m_config.GetStride();
432 
433   glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_buffer_name);
434   glPixelStorei(GL_UNPACK_ROW_LENGTH, m_config.width);
435 
436   if (!UsePersistentStagingBuffers())
437   {
438     // Unmap the buffer before writing when not using persistent mappings.
439     if (m_map_pointer)
440     {
441       glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
442       m_map_pointer = nullptr;
443     }
444   }
445   else
446   {
447     // Since we're not using coherent mapping, we must flush the range explicitly.
448     if (m_type == StagingTextureType::Upload)
449       glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, src_offset, copy_size);
450     glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT);
451   }
452 
453   // Copy from the staging buffer to the texture object.
454   const GLenum target = gltex->GetGLTarget();
455   glActiveTexture(GL_MUTABLE_TEXTURE_INDEX);
456   glBindTexture(target, gltex->GetGLTextureId());
457   glTexSubImage3D(target, 0, dst_rect.left, dst_rect.top, dst_layer, dst_rect.GetWidth(),
458                   dst_rect.GetHeight(), 1, GetGLFormatForTextureFormat(dst->GetFormat()),
459                   GetGLTypeForTextureFormat(dst->GetFormat()), reinterpret_cast<void*>(src_offset));
460 
461   glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
462 
463   // If we support buffer storage, create a fence for synchronization.
464   if (UsePersistentStagingBuffers())
465   {
466     if (m_fence != 0)
467       glDeleteSync(m_fence);
468 
469     m_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
470     glFlush();
471   }
472 
473   m_needs_flush = true;
474 }
475 
Flush()476 void OGLStagingTexture::Flush()
477 {
478   // No-op when not using buffer storage, as the transfers happen on Map().
479   // m_fence will always be zero in this case.
480   if (m_fence == 0)
481   {
482     m_needs_flush = false;
483     return;
484   }
485 
486   glClientWaitSync(m_fence, 0, GL_TIMEOUT_IGNORED);
487   glDeleteSync(m_fence);
488   m_fence = 0;
489   m_needs_flush = false;
490 }
491 
Map()492 bool OGLStagingTexture::Map()
493 {
494   if (m_map_pointer)
495     return true;
496 
497   // Slow path, map the texture, unmap it later.
498   GLenum flags;
499   if (m_type == StagingTextureType::Readback)
500     flags = GL_MAP_READ_BIT;
501   else if (m_type == StagingTextureType::Upload)
502     flags = GL_MAP_WRITE_BIT;
503   else
504     flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT;
505   glBindBuffer(m_target, m_buffer_name);
506   m_map_pointer = reinterpret_cast<char*>(glMapBufferRange(m_target, 0, m_buffer_size, flags));
507   glBindBuffer(m_target, 0);
508   return m_map_pointer != nullptr;
509 }
510 
Unmap()511 void OGLStagingTexture::Unmap()
512 {
513   // No-op with persistent mapped buffers.
514   if (!m_map_pointer || UsePersistentStagingBuffers())
515     return;
516 
517   glBindBuffer(m_target, m_buffer_name);
518   glUnmapBuffer(m_target);
519   glBindBuffer(m_target, 0);
520   m_map_pointer = nullptr;
521 }
522 
OGLFramebuffer(AbstractTexture * color_attachment,AbstractTexture * depth_attachment,AbstractTextureFormat color_format,AbstractTextureFormat depth_format,u32 width,u32 height,u32 layers,u32 samples,GLuint fbo)523 OGLFramebuffer::OGLFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment,
524                                AbstractTextureFormat color_format,
525                                AbstractTextureFormat depth_format, u32 width, u32 height,
526                                u32 layers, u32 samples, GLuint fbo)
527     : AbstractFramebuffer(color_attachment, depth_attachment, color_format, depth_format, width,
528                           height, layers, samples),
529       m_fbo(fbo)
530 {
531 }
532 
~OGLFramebuffer()533 OGLFramebuffer::~OGLFramebuffer()
534 {
535   glDeleteFramebuffers(1, &m_fbo);
536 }
537 
Create(OGLTexture * color_attachment,OGLTexture * depth_attachment)538 std::unique_ptr<OGLFramebuffer> OGLFramebuffer::Create(OGLTexture* color_attachment,
539                                                        OGLTexture* depth_attachment)
540 {
541   if (!ValidateConfig(color_attachment, depth_attachment))
542     return nullptr;
543 
544   const AbstractTextureFormat color_format =
545       color_attachment ? color_attachment->GetFormat() : AbstractTextureFormat::Undefined;
546   const AbstractTextureFormat depth_format =
547       depth_attachment ? depth_attachment->GetFormat() : AbstractTextureFormat::Undefined;
548   const OGLTexture* either_attachment = color_attachment ? color_attachment : depth_attachment;
549   const u32 width = either_attachment->GetWidth();
550   const u32 height = either_attachment->GetHeight();
551   const u32 layers = either_attachment->GetLayers();
552   const u32 samples = either_attachment->GetSamples();
553 
554   GLuint fbo;
555   glGenFramebuffers(1, &fbo);
556   glBindFramebuffer(GL_FRAMEBUFFER, fbo);
557 
558   if (color_attachment)
559   {
560     if (color_attachment->GetConfig().layers > 1)
561     {
562       glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, color_attachment->GetGLTextureId(),
563                            0);
564     }
565     else
566     {
567       glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
568                                 color_attachment->GetGLTextureId(), 0, 0);
569     }
570   }
571 
572   if (depth_attachment)
573   {
574     GLenum attachment = AbstractTexture::IsStencilFormat(depth_format) ?
575                             GL_DEPTH_STENCIL_ATTACHMENT :
576                             GL_DEPTH_ATTACHMENT;
577     if (depth_attachment->GetConfig().layers > 1)
578     {
579       glFramebufferTexture(GL_FRAMEBUFFER, attachment, depth_attachment->GetGLTextureId(), 0);
580     }
581     else
582     {
583       glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment, depth_attachment->GetGLTextureId(), 0,
584                                 0);
585     }
586   }
587 
588   DEBUG_ASSERT(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
589   Renderer::GetInstance()->RestoreFramebufferBinding();
590 
591   return std::make_unique<OGLFramebuffer>(color_attachment, depth_attachment, color_format,
592                                           depth_format, width, height, layers, samples, fbo);
593 }
594 
UpdateDimensions(u32 width,u32 height)595 void OGLFramebuffer::UpdateDimensions(u32 width, u32 height)
596 {
597   m_width = width;
598   m_height = height;
599 }
600 
601 }  // namespace OGL
602