| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #include "GLcommon/FramebufferData.h" |
| |
| #include "aemu/base/files/StreamSerializing.h" |
| #include "GLcommon/GLEScontext.h" |
| #include "GLcommon/GLutils.h" |
| #include "GLcommon/TextureData.h" |
| |
| #include <GLES/gl.h> |
| #include <GLES/glext.h> |
| |
| RenderbufferData::RenderbufferData(android::base::Stream* stream) : |
| ObjectData(stream) { |
| attachedFB = stream->getBe32(); |
| attachedPoint = stream->getBe32(); |
| // TODO: load eglImageGlobalTexObject |
| width = stream->getBe32(); |
| height = stream->getBe32(); |
| internalformat = stream->getBe32(); |
| hostInternalFormat = stream->getBe32(); |
| everBound = stream->getBe32(); |
| } |
| |
| void RenderbufferData::onSave(android::base::Stream* stream, unsigned int globalName) const { |
| ObjectData::onSave(stream, globalName); |
| stream->putBe32(attachedFB); |
| stream->putBe32(attachedPoint); |
| // TODO: snapshot eglImageGlobalTexObject |
| if (eglImageGlobalTexObject) { |
| fprintf(stderr, "RenderbufferData::onSave: warning:" |
| " EglImage snapshot unimplemented. \n"); |
| } |
| stream->putBe32(width); |
| stream->putBe32(height); |
| stream->putBe32(internalformat); |
| stream->putBe32(hostInternalFormat); |
| stream->putBe32(everBound); |
| } |
| |
| void RenderbufferData::restore(ObjectLocalName localName, |
| const getGlobalName_t& getGlobalName) { |
| ObjectData::restore(localName, getGlobalName); |
| int globalName = getGlobalName(NamedObjectType::RENDERBUFFER, |
| localName); |
| GLDispatch& dispatcher = GLEScontext::dispatcher(); |
| dispatcher.glBindRenderbuffer(GL_RENDERBUFFER, globalName); |
| if (hostInternalFormat != GL_NONE) { |
| dispatcher.glRenderbufferStorage(GL_RENDERBUFFER, hostInternalFormat, |
| width, height); |
| } |
| } |
| |
| void RenderbufferData::makeTextureDirty() { |
| if (saveableTexture) { |
| saveableTexture->makeDirty(); |
| } |
| } |
| |
| static GLenum s_index2Attachment(int idx); |
| |
| FramebufferData::FramebufferData(GLuint name, GLuint globalName) : ObjectData(FRAMEBUFFER_DATA) |
| , m_fbName(name), m_fbGlobalName(globalName) { |
| } |
| |
| FramebufferData::FramebufferData(android::base::Stream* stream) : |
| ObjectData(stream) { |
| m_fbName = stream->getBe32(); |
| int attachNum = stream->getBe32(); |
| (void)attachNum; |
| assert(attachNum == MAX_ATTACH_POINTS); |
| for (auto& attachPoint : m_attachPoints) { |
| attachPoint.target = stream->getBe32(); |
| attachPoint.name = stream->getBe32(); |
| attachPoint.objType = (NamedObjectType)stream->getBe32(); |
| // attachPoint.obj will be set up in postLoad |
| attachPoint.owned = stream->getByte(); |
| } |
| m_dirty = stream->getByte(); |
| m_hasBeenBound = stream->getByte(); |
| m_hasDrawBuffers = stream->getByte(); |
| android::base::loadBuffer(stream, &m_drawBuffers); |
| m_readBuffer = stream->getBe32(); |
| } |
| |
| FramebufferData::~FramebufferData() { |
| for (int i=0; i<MAX_ATTACH_POINTS; i++) { |
| detachObject(i); |
| } |
| } |
| |
| void FramebufferData::onSave(android::base::Stream* stream, unsigned int globalName) const { |
| ObjectData::onSave(stream, globalName); |
| stream->putBe32(m_fbName); |
| stream->putBe32(MAX_ATTACH_POINTS); |
| for (auto& attachPoint : m_attachPoints) { |
| stream->putBe32(attachPoint.target); |
| stream->putBe32(attachPoint.name); |
| // do not save attachPoint.obj |
| if (attachPoint.obj) { |
| stream->putBe32((uint32_t)ObjectDataType2NamedObjectType( |
| attachPoint.obj->getDataType())); |
| } else { |
| stream->putBe32((uint32_t)NamedObjectType::NULLTYPE); |
| } |
| stream->putByte(attachPoint.owned); |
| } |
| stream->putByte(m_dirty); |
| stream->putByte(m_hasBeenBound); |
| stream->putByte(m_hasDrawBuffers); |
| android::base::saveBuffer(stream, m_drawBuffers); |
| stream->putBe32(m_readBuffer); |
| } |
| |
| void FramebufferData::postLoad(const getObjDataPtr_t& getObjDataPtr) { |
| for (auto& attachPoint : m_attachPoints) { |
| if (NamedObjectType::NULLTYPE != attachPoint.objType) { |
| attachPoint.obj = getObjDataPtr(attachPoint.objType, |
| attachPoint.name); |
| if (!attachPoint.obj) { |
| fprintf(stderr, "FramebufferData::postLoad: warning: " |
| "bound render buffer restore failed.\n"); |
| attachPoint.obj.reset(new RenderbufferData); |
| } |
| } else { |
| attachPoint.obj = {}; |
| } |
| } |
| } |
| |
| void FramebufferData::restore(ObjectLocalName localName, |
| const getGlobalName_t& getGlobalName) { |
| ObjectData::restore(localName, getGlobalName); |
| if (!hasBeenBoundAtLeastOnce()) return; |
| int globalName = getGlobalName(NamedObjectType::FRAMEBUFFER, |
| localName); |
| GLDispatch& dispatcher = GLEScontext::dispatcher(); |
| dispatcher.glBindFramebuffer(GL_FRAMEBUFFER, globalName); |
| for (int i = 0; i < MAX_ATTACH_POINTS; i++) { |
| auto& attachPoint = m_attachPoints[i]; |
| if (!attachPoint.name) continue; // bound to nothing |
| // attachPoint.owned is true only when color buffer 0 is |
| // not bound. In such situation, it will generate its own object when |
| // calling validate() |
| if (attachPoint.owned) { |
| attachPoint.name = 0; |
| continue; |
| } |
| if (attachPoint.obj) { // binding a render buffer |
| assert(attachPoint.obj->getDataType() |
| == RENDERBUFFER_DATA); |
| attachPoint.globalName = |
| getGlobalName(NamedObjectType::RENDERBUFFER, |
| attachPoint.name); |
| RenderbufferData *rbData = (RenderbufferData*)attachPoint.obj.get(); |
| if (rbData->eglImageGlobalTexObject) { |
| fprintf(stderr, "FramebufferData::restore: warning: " |
| "binding egl image unsupported\n"); |
| } else { |
| assert(attachPoint.target == GL_RENDERBUFFER); |
| dispatcher.glFramebufferRenderbuffer( |
| GL_FRAMEBUFFER, |
| s_index2Attachment(i), |
| attachPoint.target, |
| attachPoint.globalName); |
| } |
| } else { // binding a texture |
| int texGlobalName = getGlobalName(NamedObjectType::TEXTURE, |
| attachPoint.name); |
| attachPoint.globalName = texGlobalName; |
| if (!texGlobalName) { |
| fprintf(stderr, "FramebufferData::restore: warning: " |
| "a texture is deleted without unbinding FBO\n"); |
| } |
| dispatcher.glFramebufferTexture2D(GL_FRAMEBUFFER, |
| s_index2Attachment(i), |
| attachPoint.target, |
| texGlobalName, |
| 0); |
| } |
| } |
| m_dirty = true; |
| if (m_hasDrawBuffers) { |
| dispatcher.glDrawBuffers(m_drawBuffers.size(), m_drawBuffers.data()); |
| } |
| if (dispatcher.glReadBuffer) { |
| dispatcher.glReadBuffer(m_readBuffer); |
| } |
| } |
| |
| void FramebufferData::makeTextureDirty(const getObjDataPtr_t& getObjDataPtr) { |
| if (!hasBeenBoundAtLeastOnce()) return; |
| for (int i = 0; i < MAX_ATTACH_POINTS; i++) { |
| auto& attachPoint = m_attachPoints[i]; |
| if (!attachPoint.name || attachPoint.owned || attachPoint.obj) { |
| // If not bound to a texture, do nothing |
| continue; |
| } |
| TextureData* texData = (TextureData*)getObjDataPtr( |
| NamedObjectType::TEXTURE, attachPoint.name).get(); |
| if (texData) { |
| texData->makeDirty(); |
| } |
| } |
| } |
| |
| void FramebufferData::setAttachment( |
| class GLEScontext* ctx, |
| GLenum attachment, |
| GLenum target, |
| GLuint name, |
| ObjectDataPtr obj, |
| bool takeOwnership) { |
| |
| int idx = attachmentPointIndex(attachment); |
| |
| if (!name) { |
| detachObject(idx); |
| return; |
| } |
| if (m_attachPoints[idx].target != target || |
| m_attachPoints[idx].name != name || |
| m_attachPoints[idx].obj.get() != obj.get() || |
| m_attachPoints[idx].owned != takeOwnership) { |
| detachObject(idx); |
| |
| m_attachPoints[idx].target = target; |
| m_attachPoints[idx].name = name; |
| |
| NamedObjectType namedObjectType = |
| target == GL_RENDERBUFFER ? |
| NamedObjectType::RENDERBUFFER : |
| NamedObjectType::TEXTURE; |
| |
| m_attachPoints[idx].globalName = |
| name ? ctx->shareGroup()->getGlobalName(namedObjectType, name) : 0; |
| |
| m_attachPoints[idx].obj = obj; |
| m_attachPoints[idx].owned = takeOwnership; |
| |
| if (target == GL_RENDERBUFFER_OES && obj.get() != NULL) { |
| RenderbufferData *rbData = (RenderbufferData *)obj.get(); |
| rbData->attachedFB = m_fbName; |
| rbData->attachedPoint = attachment; |
| } |
| |
| m_dirty = true; |
| |
| refreshSeparateDepthStencilAttachmentState(); |
| } |
| } |
| |
| GLuint FramebufferData::getAttachment(GLenum attachment, |
| GLenum *outTarget, |
| ObjectDataPtr *outObj) { |
| int idx = attachmentPointIndex(attachment); |
| if (outTarget) *outTarget = m_attachPoints[idx].target; |
| if (outObj) *outObj = m_attachPoints[idx].obj; |
| return m_attachPoints[idx].name; |
| } |
| |
| GLint FramebufferData::getAttachmentSamples(GLEScontext* ctx, GLenum attachment) { |
| int idx = attachmentPointIndex(attachment); |
| |
| // Don't expose own attachments. |
| if (m_attachPoints[idx].owned) return 0; |
| |
| GLenum target = m_attachPoints[idx].target; |
| GLuint name = m_attachPoints[idx].name; |
| |
| if (target == GL_RENDERBUFFER) { |
| RenderbufferData* rbData = (RenderbufferData*) |
| ctx->shareGroup()->getObjectData(NamedObjectType::RENDERBUFFER, name); |
| return rbData ? rbData->samples : 0; |
| } else { |
| TextureData* texData = (TextureData*) |
| ctx->shareGroup()->getObjectData(NamedObjectType::TEXTURE, name); |
| return texData ? texData->samples : 0; |
| } |
| } |
| |
| void FramebufferData::getAttachmentDimensions(GLEScontext* ctx, GLenum attachment, GLint* width, GLint* height) { |
| int idx = attachmentPointIndex(attachment); |
| |
| // Don't expose own attachments. |
| if (m_attachPoints[idx].owned) return; |
| |
| GLenum target = m_attachPoints[idx].target; |
| GLuint name = m_attachPoints[idx].name; |
| |
| if (target == GL_RENDERBUFFER) { |
| RenderbufferData* rbData = (RenderbufferData*) |
| ctx->shareGroup()->getObjectData(NamedObjectType::RENDERBUFFER, name); |
| if (rbData) { |
| *width = rbData->width; |
| *height = rbData->height; |
| } |
| } else { |
| TextureData* texData = (TextureData*) |
| ctx->shareGroup()->getObjectData(NamedObjectType::TEXTURE, name); |
| if (texData) { |
| *width = texData->width; |
| *height = texData->height; |
| } |
| } |
| } |
| |
| GLint FramebufferData::getAttachmentInternalFormat(GLEScontext* ctx, GLenum attachment) { |
| int idx = attachmentPointIndex(attachment); |
| |
| // Don't expose own attachments. |
| if (m_attachPoints[idx].owned) return 0; |
| |
| GLenum target = m_attachPoints[idx].target; |
| GLuint name = m_attachPoints[idx].name; |
| |
| if (target == GL_RENDERBUFFER) { |
| RenderbufferData* rbData = (RenderbufferData*) |
| ctx->shareGroup()->getObjectData(NamedObjectType::RENDERBUFFER, name); |
| return rbData ? rbData->internalformat : 0; |
| } else { |
| TextureData* texData = (TextureData*) |
| ctx->shareGroup()->getObjectData(NamedObjectType::TEXTURE, name); |
| return texData? texData->internalFormat : 0; |
| } |
| } |
| |
| void FramebufferData::separateDepthStencilWorkaround(GLEScontext* ctx) { |
| // Swiftshader does not need the workaround as it allows separate depth/stencil. |
| if (isGles2Gles()) return; |
| |
| // bug: 78083376 |
| // |
| // Some apps rely on using separate depth/stencil attachments with separate |
| // backing images. This affects macOS OpenGL because it does not allow |
| // separate depth/stencil attachments with separate backing images. |
| // |
| // Emulate them here with a single combined backing image. |
| #ifdef __APPLE__ |
| if (!m_hasSeparateDepthStencil || m_separateDSEmulationRbo) return; |
| |
| GLuint prevRboBinding; |
| GLuint prevFboBinding; |
| auto& gl = ctx->dispatcher(); |
| // Use the depth stencil's dimensions. |
| GLint widthDepth; |
| GLint heightDepth; |
| getAttachmentDimensions(ctx, GL_DEPTH_ATTACHMENT, |
| &widthDepth, &heightDepth); |
| |
| gl.glGetIntegerv(GL_RENDERBUFFER_BINDING, (GLint*)&prevRboBinding); |
| gl.glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint*)&prevFboBinding); |
| |
| gl.glGenRenderbuffers(1, &m_separateDSEmulationRbo); |
| gl.glBindRenderbuffer(GL_RENDERBUFFER, m_separateDSEmulationRbo); |
| gl.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, |
| widthDepth, heightDepth); |
| |
| gl.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbGlobalName); |
| gl.glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, |
| GL_RENDERBUFFER, m_separateDSEmulationRbo); |
| gl.glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, |
| GL_RENDERBUFFER, m_separateDSEmulationRbo); |
| |
| gl.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prevFboBinding); |
| gl.glBindRenderbuffer(GL_RENDERBUFFER, prevRboBinding); |
| #endif |
| } |
| |
| int FramebufferData::attachmentPointIndex(GLenum attachment) |
| { |
| switch(attachment) { |
| case GL_COLOR_ATTACHMENT0_OES: |
| return 0; |
| case GL_DEPTH_ATTACHMENT_OES: |
| return 1; |
| case GL_STENCIL_ATTACHMENT_OES: |
| return 2; |
| case GL_DEPTH_STENCIL_ATTACHMENT: |
| return 3; |
| default: |
| { |
| // for colorbuffer 1 ~ 15, they are continuous |
| int idx = attachment - GL_COLOR_ATTACHMENT1 + 4; |
| // in case for some new attachment extensions |
| if (idx < 4 || idx > MAX_ATTACH_POINTS) { |
| idx = MAX_ATTACH_POINTS; |
| } |
| return idx; |
| } |
| } |
| } |
| |
| static GLenum s_index2Attachment(int idx) { |
| switch (idx) { |
| case 0: |
| return GL_COLOR_ATTACHMENT0_OES; |
| case 1: |
| return GL_DEPTH_ATTACHMENT_OES; |
| case 2: |
| return GL_STENCIL_ATTACHMENT_OES; |
| case 3: |
| return GL_DEPTH_STENCIL_ATTACHMENT; |
| default: |
| return idx - 4 + GL_COLOR_ATTACHMENT1; |
| } |
| } |
| |
| void FramebufferData::detachObject(int idx) { |
| if (m_attachPoints[idx].target == GL_RENDERBUFFER_OES && m_attachPoints[idx].obj.get() != NULL) { |
| RenderbufferData *rbData = (RenderbufferData *)m_attachPoints[idx].obj.get(); |
| rbData->attachedFB = 0; |
| rbData->attachedPoint = 0; |
| } |
| |
| if(m_attachPoints[idx].owned) |
| { |
| switch(m_attachPoints[idx].target) |
| { |
| case GL_RENDERBUFFER_OES: |
| GLEScontext::dispatcher().glDeleteRenderbuffers(1, &(m_attachPoints[idx].name)); |
| break; |
| case GL_TEXTURE_2D: |
| GLEScontext::dispatcher().glDeleteTextures(1, &(m_attachPoints[idx].name)); |
| break; |
| } |
| } |
| |
| m_attachPoints[idx] = {}; |
| |
| refreshSeparateDepthStencilAttachmentState(); |
| } |
| |
| // bug: 78083376 |
| // |
| // Check attachment state and delete / recreate original depth/stencil |
| // attachments if necessary. |
| // |
| void FramebufferData::refreshSeparateDepthStencilAttachmentState() { |
| m_hasSeparateDepthStencil = false; |
| |
| ObjectDataPtr depthObject = |
| m_attachPoints[attachmentPointIndex(GL_DEPTH_ATTACHMENT)].obj; |
| ObjectDataPtr stencilObject = |
| m_attachPoints[attachmentPointIndex(GL_STENCIL_ATTACHMENT)].obj; |
| |
| m_hasSeparateDepthStencil = depthObject && stencilObject && (depthObject != stencilObject); |
| |
| if (m_hasSeparateDepthStencil) return; |
| |
| // Delete the emulated RBO and restore the original |
| // if we don't have separate depth/stencil anymore. |
| auto& gl = GLEScontext::dispatcher(); |
| |
| if (!m_separateDSEmulationRbo) return; |
| |
| gl.glDeleteRenderbuffers(1, &m_separateDSEmulationRbo); |
| m_separateDSEmulationRbo = 0; |
| |
| // Now that we don't have separate depth/stencil attachments, |
| // we might need to restore one of the original attachments, |
| // because we were using a nonzero m_separateDSEmulationRbo. |
| GLenum attachmentToRestore = |
| m_attachPoints[attachmentPointIndex(GL_DEPTH_ATTACHMENT)].name ? |
| GL_DEPTH_ATTACHMENT : ( |
| m_attachPoints[attachmentPointIndex(GL_STENCIL_ATTACHMENT)].name ? |
| GL_STENCIL_ATTACHMENT : 0); |
| |
| if (!attachmentToRestore) return; |
| |
| GLuint objectToRestore = |
| m_attachPoints[attachmentPointIndex(attachmentToRestore)].globalName; |
| |
| GLenum objectTypeToRestore = |
| m_attachPoints[attachmentPointIndex(attachmentToRestore)].target; |
| |
| GLuint prevFboBinding; |
| gl.glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint*)&prevFboBinding); |
| gl.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbGlobalName); |
| |
| switch (objectTypeToRestore) { |
| case GL_RENDERBUFFER: |
| gl.glFramebufferRenderbuffer( |
| GL_DRAW_FRAMEBUFFER, |
| attachmentToRestore, |
| GL_RENDERBUFFER, |
| objectToRestore); |
| break; |
| case GL_TEXTURE_2D: |
| gl.glFramebufferTexture2D( |
| GL_DRAW_FRAMEBUFFER, |
| attachmentToRestore, |
| GL_TEXTURE_2D, |
| objectToRestore, 0); |
| break; |
| } |
| |
| gl.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prevFboBinding); |
| } |
| |
| void FramebufferData::validate(GLEScontext* ctx) |
| { |
| // Do not validate if on another GLES2 backend |
| if (isGles2Gles()) return; |
| if(!getAttachment(GL_COLOR_ATTACHMENT0_OES, NULL, NULL)) |
| { |
| // GLES does not require the framebuffer to have a color attachment. |
| // OpenGL does. Therefore, if no color is attached, create a dummy |
| // color texture and attach it. |
| // This dummy color texture will is owned by the FramebufferObject, |
| // and will be released by it when its object is detached. |
| |
| GLint type = GL_NONE; |
| GLint name = 0; |
| |
| ctx->dispatcher().glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT_OES, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &type); |
| if(type != GL_NONE) |
| { |
| ctx->dispatcher().glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT_OES, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &name); |
| } |
| else |
| { |
| ctx->dispatcher().glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT_OES, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &type); |
| if(type != GL_NONE) |
| { |
| ctx->dispatcher().glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT_OES, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &name); |
| } |
| else |
| { |
| // No color, depth or stencil attachments - do nothing |
| return; |
| } |
| } |
| |
| // Find the existing attachment(s) dimensions |
| GLint width = 0; |
| GLint height = 0; |
| |
| if(type == GL_RENDERBUFFER) |
| { |
| GLint prev; |
| ctx->dispatcher().glGetIntegerv(GL_RENDERBUFFER_BINDING, &prev); |
| ctx->dispatcher().glBindRenderbuffer(GL_RENDERBUFFER, name); |
| ctx->dispatcher().glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width); |
| ctx->dispatcher().glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height); |
| ctx->dispatcher().glBindRenderbuffer(GL_RENDERBUFFER, prev); |
| } |
| else if(type == GL_TEXTURE) |
| { |
| GLint prev; |
| ctx->dispatcher().glGetIntegerv(GL_TEXTURE_BINDING_2D, &prev); |
| ctx->dispatcher().glBindTexture(GL_TEXTURE_2D, name); |
| ctx->dispatcher().glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); |
| ctx->dispatcher().glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); |
| ctx->dispatcher().glBindTexture(GL_TEXTURE_2D, prev); |
| } |
| |
| // Create the color attachment and attch it |
| unsigned int tex = 0; |
| ctx->dispatcher().glGenTextures(1, &tex); |
| GLint prev; |
| ctx->dispatcher().glGetIntegerv(GL_TEXTURE_BINDING_2D, &prev); |
| ctx->dispatcher().glBindTexture(GL_TEXTURE_2D, tex); |
| |
| ctx->dispatcher().glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); |
| ctx->dispatcher().glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); |
| ctx->dispatcher().glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); |
| ctx->dispatcher().glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); |
| ctx->dispatcher().glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); |
| |
| ctx->dispatcher().glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tex, 0); |
| setAttachment(ctx, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tex, ObjectDataPtr(), true); |
| |
| ctx->dispatcher().glBindTexture(GL_TEXTURE_2D, prev); |
| } |
| |
| if(m_dirty) |
| { |
| // This is a workaround for a bug found in several OpenGL |
| // drivers (e.g. ATI's) - after the framebuffer attachments |
| // have changed, and before the next draw, unbind and rebind |
| // the framebuffer to sort things out. |
| ctx->dispatcher().glBindFramebuffer(GL_FRAMEBUFFER,0); |
| ctx->dispatcher().glBindFramebuffer( |
| GL_FRAMEBUFFER, m_fbGlobalName); |
| |
| m_dirty = false; |
| } |
| } |
| |
| void FramebufferData::setDrawBuffers(GLsizei n, const GLenum * bufs) { |
| m_drawBuffers.resize(n); |
| memcpy(m_drawBuffers.data(), bufs, n * sizeof(GLenum)); |
| m_hasDrawBuffers = true; |
| } |
| |
| void FramebufferData::setReadBuffers(GLenum src) { |
| m_readBuffer = src; |
| } |