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