• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "GLcommon/FramebufferData.h"
17 
18 #include "aemu/base/files/StreamSerializing.h"
19 #include "GLcommon/GLEScontext.h"
20 #include "GLcommon/GLutils.h"
21 #include "GLcommon/TextureData.h"
22 
23 #include <GLES/gl.h>
24 #include <GLES/glext.h>
25 
RenderbufferData(android::base::Stream * stream)26 RenderbufferData::RenderbufferData(android::base::Stream* stream) :
27     ObjectData(stream) {
28     attachedFB = stream->getBe32();
29     attachedPoint = stream->getBe32();
30     // TODO: load eglImageGlobalTexObject
31     width = stream->getBe32();
32     height = stream->getBe32();
33     internalformat = stream->getBe32();
34     hostInternalFormat = stream->getBe32();
35     everBound = stream->getBe32();
36 }
37 
onSave(android::base::Stream * stream,unsigned int globalName) const38 void RenderbufferData::onSave(android::base::Stream* stream, unsigned int globalName) const {
39     ObjectData::onSave(stream, globalName);
40     stream->putBe32(attachedFB);
41     stream->putBe32(attachedPoint);
42     // TODO: snapshot eglImageGlobalTexObject
43     if (eglImageGlobalTexObject) {
44         fprintf(stderr, "RenderbufferData::onSave: warning:"
45                 " EglImage snapshot unimplemented. \n");
46     }
47     stream->putBe32(width);
48     stream->putBe32(height);
49     stream->putBe32(internalformat);
50     stream->putBe32(hostInternalFormat);
51     stream->putBe32(everBound);
52 }
53 
restore(ObjectLocalName localName,const getGlobalName_t & getGlobalName)54 void RenderbufferData::restore(ObjectLocalName localName,
55            const getGlobalName_t& getGlobalName) {
56     ObjectData::restore(localName, getGlobalName);
57     int globalName = getGlobalName(NamedObjectType::RENDERBUFFER,
58             localName);
59     GLDispatch& dispatcher = GLEScontext::dispatcher();
60     dispatcher.glBindRenderbuffer(GL_RENDERBUFFER, globalName);
61     if (hostInternalFormat != GL_NONE) {
62         dispatcher.glRenderbufferStorage(GL_RENDERBUFFER, hostInternalFormat,
63                                          width, height);
64     }
65 }
66 
makeTextureDirty()67 void RenderbufferData::makeTextureDirty() {
68     if (saveableTexture) {
69         saveableTexture->makeDirty();
70     }
71 }
72 
73 static GLenum s_index2Attachment(int idx);
74 
FramebufferData(GLuint name,GLuint globalName)75 FramebufferData::FramebufferData(GLuint name, GLuint globalName) : ObjectData(FRAMEBUFFER_DATA)
76         , m_fbName(name), m_fbGlobalName(globalName) {
77 }
78 
FramebufferData(android::base::Stream * stream)79 FramebufferData::FramebufferData(android::base::Stream* stream) :
80     ObjectData(stream) {
81     m_fbName = stream->getBe32();
82     int attachNum = stream->getBe32();
83     (void)attachNum;
84     assert(attachNum == MAX_ATTACH_POINTS);
85     for (auto& attachPoint : m_attachPoints) {
86         attachPoint.target = stream->getBe32();
87         attachPoint.name = stream->getBe32();
88         attachPoint.objType = (NamedObjectType)stream->getBe32();
89         // attachPoint.obj will be set up in postLoad
90         attachPoint.owned = stream->getByte();
91     }
92     m_dirty = stream->getByte();
93     m_hasBeenBound = stream->getByte();
94     m_hasDrawBuffers = stream->getByte();
95     android::base::loadBuffer(stream, &m_drawBuffers);
96     m_readBuffer = stream->getBe32();
97 }
98 
~FramebufferData()99 FramebufferData::~FramebufferData() {
100     for (int i=0; i<MAX_ATTACH_POINTS; i++) {
101         detachObject(i);
102     }
103 }
104 
onSave(android::base::Stream * stream,unsigned int globalName) const105 void FramebufferData::onSave(android::base::Stream* stream, unsigned int globalName) const {
106     ObjectData::onSave(stream, globalName);
107     stream->putBe32(m_fbName);
108     stream->putBe32(MAX_ATTACH_POINTS);
109     for (auto& attachPoint : m_attachPoints) {
110         stream->putBe32(attachPoint.target);
111         stream->putBe32(attachPoint.name);
112         // do not save attachPoint.obj
113         if (attachPoint.obj) {
114             stream->putBe32((uint32_t)ObjectDataType2NamedObjectType(
115                     attachPoint.obj->getDataType()));
116         } else {
117             stream->putBe32((uint32_t)NamedObjectType::NULLTYPE);
118         }
119         stream->putByte(attachPoint.owned);
120     }
121     stream->putByte(m_dirty);
122     stream->putByte(m_hasBeenBound);
123     stream->putByte(m_hasDrawBuffers);
124     android::base::saveBuffer(stream, m_drawBuffers);
125     stream->putBe32(m_readBuffer);
126 }
127 
postLoad(const getObjDataPtr_t & getObjDataPtr)128 void FramebufferData::postLoad(const getObjDataPtr_t& getObjDataPtr) {
129     for (auto& attachPoint : m_attachPoints) {
130         if (NamedObjectType::NULLTYPE != attachPoint.objType) {
131             attachPoint.obj = getObjDataPtr(attachPoint.objType,
132                     attachPoint.name);
133             if (!attachPoint.obj) {
134                 fprintf(stderr, "FramebufferData::postLoad: warning: "
135                         "bound render buffer restore failed.\n");
136                 attachPoint.obj.reset(new RenderbufferData);
137             }
138         } else {
139             attachPoint.obj = {};
140         }
141     }
142 }
143 
restore(ObjectLocalName localName,const getGlobalName_t & getGlobalName)144 void FramebufferData::restore(ObjectLocalName localName,
145            const getGlobalName_t& getGlobalName) {
146     ObjectData::restore(localName, getGlobalName);
147     if (!hasBeenBoundAtLeastOnce()) return;
148     int globalName = getGlobalName(NamedObjectType::FRAMEBUFFER,
149             localName);
150     GLDispatch& dispatcher = GLEScontext::dispatcher();
151     dispatcher.glBindFramebuffer(GL_FRAMEBUFFER, globalName);
152     for (int i = 0; i < MAX_ATTACH_POINTS; i++) {
153         auto& attachPoint = m_attachPoints[i];
154         if (!attachPoint.name) continue; // bound to nothing
155         // attachPoint.owned is true only when color buffer 0 is
156         // not bound. In such situation, it will generate its own object when
157         // calling validate()
158         if (attachPoint.owned) {
159             attachPoint.name = 0;
160             continue;
161         }
162         if (attachPoint.obj) { // binding a render buffer
163             assert(attachPoint.obj->getDataType()
164                     == RENDERBUFFER_DATA);
165             attachPoint.globalName =
166                 getGlobalName(NamedObjectType::RENDERBUFFER,
167                               attachPoint.name);
168             RenderbufferData *rbData = (RenderbufferData*)attachPoint.obj.get();
169             if (rbData->eglImageGlobalTexObject) {
170                 fprintf(stderr, "FramebufferData::restore: warning: "
171                         "binding egl image unsupported\n");
172             } else {
173                 assert(attachPoint.target == GL_RENDERBUFFER);
174                 dispatcher.glFramebufferRenderbuffer(
175                         GL_FRAMEBUFFER,
176                         s_index2Attachment(i),
177                         attachPoint.target,
178                         attachPoint.globalName);
179             }
180         } else { // binding a texture
181             int texGlobalName = getGlobalName(NamedObjectType::TEXTURE,
182                     attachPoint.name);
183             attachPoint.globalName = texGlobalName;
184             if (!texGlobalName) {
185                 fprintf(stderr, "FramebufferData::restore: warning: "
186                         "a texture is deleted without unbinding FBO\n");
187             }
188             dispatcher.glFramebufferTexture2D(GL_FRAMEBUFFER,
189                     s_index2Attachment(i),
190                     attachPoint.target,
191                     texGlobalName,
192                     0);
193         }
194     }
195     m_dirty = true;
196     if (m_hasDrawBuffers) {
197         dispatcher.glDrawBuffers(m_drawBuffers.size(), m_drawBuffers.data());
198     }
199     if (dispatcher.glReadBuffer) {
200         dispatcher.glReadBuffer(m_readBuffer);
201     }
202 }
203 
makeTextureDirty(const getObjDataPtr_t & getObjDataPtr)204 void FramebufferData::makeTextureDirty(const getObjDataPtr_t& getObjDataPtr) {
205     if (!hasBeenBoundAtLeastOnce()) return;
206     for (int i = 0; i < MAX_ATTACH_POINTS; i++) {
207         auto& attachPoint = m_attachPoints[i];
208         if (!attachPoint.name || attachPoint.owned || attachPoint.obj) {
209             // If not bound to a texture, do nothing
210             continue;
211         }
212         TextureData* texData = (TextureData*)getObjDataPtr(
213             NamedObjectType::TEXTURE, attachPoint.name).get();
214         if (texData) {
215             texData->makeDirty();
216         }
217     }
218 }
219 
setAttachment(class GLEScontext * ctx,GLenum attachment,GLenum target,GLuint name,ObjectDataPtr obj,bool takeOwnership)220 void FramebufferData::setAttachment(
221                class GLEScontext* ctx,
222                GLenum attachment,
223                GLenum target,
224                GLuint name,
225                ObjectDataPtr obj,
226                bool takeOwnership) {
227 
228     int idx = attachmentPointIndex(attachment);
229 
230     if (!name) {
231         detachObject(idx);
232         return;
233     }
234     if (m_attachPoints[idx].target != target ||
235         m_attachPoints[idx].name != name ||
236         m_attachPoints[idx].obj.get() != obj.get() ||
237         m_attachPoints[idx].owned != takeOwnership) {
238         detachObject(idx);
239 
240         m_attachPoints[idx].target = target;
241         m_attachPoints[idx].name = name;
242 
243         NamedObjectType namedObjectType =
244             target == GL_RENDERBUFFER ?
245                 NamedObjectType::RENDERBUFFER :
246                 NamedObjectType::TEXTURE;
247 
248         m_attachPoints[idx].globalName =
249             name ? ctx->shareGroup()->getGlobalName(namedObjectType, name) : 0;
250 
251         m_attachPoints[idx].obj = obj;
252         m_attachPoints[idx].owned = takeOwnership;
253 
254         if (target == GL_RENDERBUFFER_OES && obj.get() != NULL) {
255             RenderbufferData *rbData = (RenderbufferData *)obj.get();
256             rbData->attachedFB = m_fbName;
257             rbData->attachedPoint = attachment;
258         }
259 
260         m_dirty = true;
261 
262         refreshSeparateDepthStencilAttachmentState();
263     }
264 }
265 
getAttachment(GLenum attachment,GLenum * outTarget,ObjectDataPtr * outObj)266 GLuint FramebufferData::getAttachment(GLenum attachment,
267                  GLenum *outTarget,
268                  ObjectDataPtr *outObj) {
269     int idx = attachmentPointIndex(attachment);
270     if (outTarget) *outTarget = m_attachPoints[idx].target;
271     if (outObj) *outObj = m_attachPoints[idx].obj;
272     return m_attachPoints[idx].name;
273 }
274 
getAttachmentSamples(GLEScontext * ctx,GLenum attachment)275 GLint FramebufferData::getAttachmentSamples(GLEScontext* ctx, GLenum attachment) {
276     int idx = attachmentPointIndex(attachment);
277 
278     // Don't expose own attachments.
279     if (m_attachPoints[idx].owned) return 0;
280 
281     GLenum target = m_attachPoints[idx].target;
282     GLuint name = m_attachPoints[idx].name;
283 
284     if (target == GL_RENDERBUFFER) {
285         RenderbufferData* rbData = (RenderbufferData*)
286             ctx->shareGroup()->getObjectData(NamedObjectType::RENDERBUFFER, name);
287         return rbData ? rbData->samples : 0;
288     } else {
289         TextureData* texData = (TextureData*)
290             ctx->shareGroup()->getObjectData(NamedObjectType::TEXTURE, name);
291         return texData ? texData->samples : 0;
292     }
293 }
294 
getAttachmentDimensions(GLEScontext * ctx,GLenum attachment,GLint * width,GLint * height)295 void FramebufferData::getAttachmentDimensions(GLEScontext* ctx, GLenum attachment, GLint* width, GLint* height) {
296     int idx = attachmentPointIndex(attachment);
297 
298     // Don't expose own attachments.
299     if (m_attachPoints[idx].owned) return;
300 
301     GLenum target = m_attachPoints[idx].target;
302     GLuint name = m_attachPoints[idx].name;
303 
304     if (target == GL_RENDERBUFFER) {
305         RenderbufferData* rbData = (RenderbufferData*)
306             ctx->shareGroup()->getObjectData(NamedObjectType::RENDERBUFFER, name);
307         if (rbData) {
308             *width = rbData->width;
309             *height = rbData->height;
310         }
311     } else {
312         TextureData* texData = (TextureData*)
313             ctx->shareGroup()->getObjectData(NamedObjectType::TEXTURE, name);
314         if (texData) {
315             *width = texData->width;
316             *height = texData->height;
317         }
318     }
319 }
320 
getAttachmentInternalFormat(GLEScontext * ctx,GLenum attachment)321 GLint FramebufferData::getAttachmentInternalFormat(GLEScontext* ctx, GLenum attachment) {
322     int idx = attachmentPointIndex(attachment);
323 
324     // Don't expose own attachments.
325     if (m_attachPoints[idx].owned) return 0;
326 
327     GLenum target = m_attachPoints[idx].target;
328     GLuint name = m_attachPoints[idx].name;
329 
330     if (target == GL_RENDERBUFFER) {
331         RenderbufferData* rbData = (RenderbufferData*)
332             ctx->shareGroup()->getObjectData(NamedObjectType::RENDERBUFFER, name);
333         return rbData ? rbData->internalformat : 0;
334     } else {
335         TextureData* texData = (TextureData*)
336             ctx->shareGroup()->getObjectData(NamedObjectType::TEXTURE, name);
337         return texData? texData->internalFormat : 0;
338     }
339 }
340 
separateDepthStencilWorkaround(GLEScontext * ctx)341 void FramebufferData::separateDepthStencilWorkaround(GLEScontext* ctx) {
342     // Swiftshader does not need the workaround as it allows separate depth/stencil.
343     if (isGles2Gles()) return;
344 
345     // bug: 78083376
346     //
347     // Some apps rely on using separate depth/stencil attachments with separate
348     // backing images. This affects macOS OpenGL because it does not allow
349     // separate depth/stencil attachments with separate backing images.
350     //
351     // Emulate them here with a single combined backing image.
352 #ifdef __APPLE__
353     if (!m_hasSeparateDepthStencil || m_separateDSEmulationRbo) return;
354 
355     GLuint prevRboBinding;
356     GLuint prevFboBinding;
357     auto& gl = ctx->dispatcher();
358     // Use the depth stencil's dimensions.
359     GLint widthDepth;
360     GLint heightDepth;
361     getAttachmentDimensions(ctx, GL_DEPTH_ATTACHMENT,
362                             &widthDepth, &heightDepth);
363 
364     gl.glGetIntegerv(GL_RENDERBUFFER_BINDING, (GLint*)&prevRboBinding);
365     gl.glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint*)&prevFboBinding);
366 
367     gl.glGenRenderbuffers(1, &m_separateDSEmulationRbo);
368     gl.glBindRenderbuffer(GL_RENDERBUFFER, m_separateDSEmulationRbo);
369     gl.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8,
370                              widthDepth, heightDepth);
371 
372     gl.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbGlobalName);
373     gl.glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
374                                  GL_RENDERBUFFER, m_separateDSEmulationRbo);
375     gl.glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
376                                  GL_RENDERBUFFER, m_separateDSEmulationRbo);
377 
378     gl.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prevFboBinding);
379     gl.glBindRenderbuffer(GL_RENDERBUFFER, prevRboBinding);
380 #endif
381 }
382 
attachmentPointIndex(GLenum attachment)383 int FramebufferData::attachmentPointIndex(GLenum attachment)
384 {
385     switch(attachment) {
386     case GL_COLOR_ATTACHMENT0_OES:
387         return 0;
388     case GL_DEPTH_ATTACHMENT_OES:
389         return 1;
390     case GL_STENCIL_ATTACHMENT_OES:
391         return 2;
392     case GL_DEPTH_STENCIL_ATTACHMENT:
393         return 3;
394     default:
395         {
396             // for colorbuffer 1 ~ 15, they are continuous
397             int idx = attachment - GL_COLOR_ATTACHMENT1 + 4;
398             // in case for some new attachment extensions
399             if (idx < 4 || idx > MAX_ATTACH_POINTS) {
400                 idx = MAX_ATTACH_POINTS;
401             }
402             return idx;
403         }
404     }
405 }
406 
s_index2Attachment(int idx)407 static GLenum s_index2Attachment(int idx) {
408     switch (idx) {
409     case 0:
410         return GL_COLOR_ATTACHMENT0_OES;
411     case 1:
412         return GL_DEPTH_ATTACHMENT_OES;
413     case 2:
414         return GL_STENCIL_ATTACHMENT_OES;
415     case 3:
416         return GL_DEPTH_STENCIL_ATTACHMENT;
417     default:
418         return idx - 4 + GL_COLOR_ATTACHMENT1;
419     }
420 }
421 
detachObject(int idx)422 void FramebufferData::detachObject(int idx) {
423     if (m_attachPoints[idx].target == GL_RENDERBUFFER_OES && m_attachPoints[idx].obj.get() != NULL) {
424         RenderbufferData *rbData = (RenderbufferData *)m_attachPoints[idx].obj.get();
425         rbData->attachedFB = 0;
426         rbData->attachedPoint = 0;
427     }
428 
429     if(m_attachPoints[idx].owned)
430     {
431         switch(m_attachPoints[idx].target)
432         {
433         case GL_RENDERBUFFER_OES:
434             GLEScontext::dispatcher().glDeleteRenderbuffers(1, &(m_attachPoints[idx].name));
435             break;
436         case GL_TEXTURE_2D:
437             GLEScontext::dispatcher().glDeleteTextures(1, &(m_attachPoints[idx].name));
438             break;
439         }
440     }
441 
442     m_attachPoints[idx] = {};
443 
444     refreshSeparateDepthStencilAttachmentState();
445 }
446 
447 // bug: 78083376
448 //
449 // Check attachment state and delete / recreate original depth/stencil
450 // attachments if necessary.
451 //
refreshSeparateDepthStencilAttachmentState()452 void FramebufferData::refreshSeparateDepthStencilAttachmentState() {
453     m_hasSeparateDepthStencil = false;
454 
455     ObjectDataPtr depthObject =
456         m_attachPoints[attachmentPointIndex(GL_DEPTH_ATTACHMENT)].obj;
457     ObjectDataPtr stencilObject =
458         m_attachPoints[attachmentPointIndex(GL_STENCIL_ATTACHMENT)].obj;
459 
460     m_hasSeparateDepthStencil = depthObject && stencilObject && (depthObject != stencilObject);
461 
462     if (m_hasSeparateDepthStencil) return;
463 
464     // Delete the emulated RBO and restore the original
465     // if we don't have separate depth/stencil anymore.
466     auto& gl = GLEScontext::dispatcher();
467 
468     if (!m_separateDSEmulationRbo) return;
469 
470     gl.glDeleteRenderbuffers(1, &m_separateDSEmulationRbo);
471     m_separateDSEmulationRbo = 0;
472 
473     // Now that we don't have separate depth/stencil attachments,
474     // we might need to restore one of the original attachments,
475     // because we were using a nonzero m_separateDSEmulationRbo.
476     GLenum attachmentToRestore =
477         m_attachPoints[attachmentPointIndex(GL_DEPTH_ATTACHMENT)].name ?
478             GL_DEPTH_ATTACHMENT : (
479                 m_attachPoints[attachmentPointIndex(GL_STENCIL_ATTACHMENT)].name ?
480                     GL_STENCIL_ATTACHMENT : 0);
481 
482     if (!attachmentToRestore) return;
483 
484     GLuint objectToRestore =
485         m_attachPoints[attachmentPointIndex(attachmentToRestore)].globalName;
486 
487     GLenum objectTypeToRestore =
488         m_attachPoints[attachmentPointIndex(attachmentToRestore)].target;
489 
490     GLuint prevFboBinding;
491     gl.glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint*)&prevFboBinding);
492     gl.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbGlobalName);
493 
494     switch (objectTypeToRestore) {
495     case GL_RENDERBUFFER:
496         gl.glFramebufferRenderbuffer(
497             GL_DRAW_FRAMEBUFFER,
498             attachmentToRestore,
499             GL_RENDERBUFFER,
500             objectToRestore);
501         break;
502     case GL_TEXTURE_2D:
503         gl.glFramebufferTexture2D(
504             GL_DRAW_FRAMEBUFFER,
505             attachmentToRestore,
506             GL_TEXTURE_2D,
507             objectToRestore, 0);
508         break;
509     }
510 
511     gl.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prevFboBinding);
512 }
513 
validate(GLEScontext * ctx)514 void FramebufferData::validate(GLEScontext* ctx)
515 {
516     // Do not validate if on another GLES2 backend
517     if (isGles2Gles()) return;
518     if(!getAttachment(GL_COLOR_ATTACHMENT0_OES, NULL, NULL))
519     {
520         // GLES does not require the framebuffer to have a color attachment.
521         // OpenGL does. Therefore, if no color is attached, create a dummy
522         // color texture and attach it.
523         // This dummy color texture will is owned by the FramebufferObject,
524         // and will be released by it when its object is detached.
525 
526         GLint type = GL_NONE;
527         GLint name = 0;
528 
529         ctx->dispatcher().glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT_OES, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &type);
530         if(type != GL_NONE)
531         {
532             ctx->dispatcher().glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT_OES, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &name);
533         }
534         else
535         {
536             ctx->dispatcher().glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT_OES, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &type);
537             if(type != GL_NONE)
538             {
539                 ctx->dispatcher().glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT_OES, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &name);
540             }
541             else
542             {
543                 // No color, depth or stencil attachments - do nothing
544                 return;
545             }
546         }
547 
548         // Find the existing attachment(s) dimensions
549         GLint width = 0;
550         GLint height = 0;
551 
552         if(type == GL_RENDERBUFFER)
553         {
554             GLint prev;
555             ctx->dispatcher().glGetIntegerv(GL_RENDERBUFFER_BINDING, &prev);
556             ctx->dispatcher().glBindRenderbuffer(GL_RENDERBUFFER, name);
557             ctx->dispatcher().glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
558             ctx->dispatcher().glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
559             ctx->dispatcher().glBindRenderbuffer(GL_RENDERBUFFER, prev);
560         }
561         else if(type == GL_TEXTURE)
562         {
563             GLint prev;
564             ctx->dispatcher().glGetIntegerv(GL_TEXTURE_BINDING_2D, &prev);
565             ctx->dispatcher().glBindTexture(GL_TEXTURE_2D, name);
566             ctx->dispatcher().glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
567             ctx->dispatcher().glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
568             ctx->dispatcher().glBindTexture(GL_TEXTURE_2D, prev);
569         }
570 
571         // Create the color attachment and attch it
572         unsigned int tex = 0;
573         ctx->dispatcher().glGenTextures(1, &tex);
574         GLint prev;
575         ctx->dispatcher().glGetIntegerv(GL_TEXTURE_BINDING_2D, &prev);
576         ctx->dispatcher().glBindTexture(GL_TEXTURE_2D, tex);
577 
578         ctx->dispatcher().glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
579         ctx->dispatcher().glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
580         ctx->dispatcher().glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
581         ctx->dispatcher().glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
582         ctx->dispatcher().glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
583 
584         ctx->dispatcher().glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tex, 0);
585         setAttachment(ctx, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tex, ObjectDataPtr(), true);
586 
587         ctx->dispatcher().glBindTexture(GL_TEXTURE_2D, prev);
588     }
589 
590     if(m_dirty)
591     {
592         // This is a workaround for a bug found in several OpenGL
593         // drivers (e.g. ATI's) - after the framebuffer attachments
594         // have changed, and before the next draw, unbind and rebind
595         // the framebuffer to sort things out.
596         ctx->dispatcher().glBindFramebuffer(GL_FRAMEBUFFER,0);
597         ctx->dispatcher().glBindFramebuffer(
598                 GL_FRAMEBUFFER, m_fbGlobalName);
599 
600         m_dirty = false;
601     }
602 }
603 
setDrawBuffers(GLsizei n,const GLenum * bufs)604 void FramebufferData::setDrawBuffers(GLsizei n, const GLenum * bufs) {
605     m_drawBuffers.resize(n);
606     memcpy(m_drawBuffers.data(), bufs, n * sizeof(GLenum));
607     m_hasDrawBuffers = true;
608 }
609 
setReadBuffers(GLenum src)610 void FramebufferData::setReadBuffers(GLenum src) {
611     m_readBuffer = src;
612 }
613