/* Copyright (c) 2013 yvt This file is part of OpenSpades. OpenSpades is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenSpades is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenSpades. If not, see . */ #include "GLFramebufferManager.h" #include "IGLDevice.h" #include "../Core/Settings.h" #include "../Core/Debug.h" #include "../Core/Debug.h" #include "../Core/Exception.h" SPADES_SETTING(r_multisamples, "0"); SPADES_SETTING(r_depthBits, ""); // TODO: use this value SPADES_SETTING(r_colorBits, ""); // TOOD: use this value SPADES_SETTING(r_srgb, "0"); SPADES_SETTING(r_highPrec, "1"); SPADES_SETTING(r_blitFramebuffer, "1"); SPADES_SETTING(r_water, "2"); namespace spades { namespace draw { static void RaiseFBStatusError(IGLDevice::Enum status) { std::string type; switch(status){ case IGLDevice::FramebufferComplete: type = "GL_FRAMEBUFFER_COMPLETE"; break; case IGLDevice::FramebufferUndefined: type = "GL_FRAMEBUFFER_UNDEFINED"; break; case IGLDevice::FramebufferIncompleteAttachment: type = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; break; case IGLDevice:: FramebufferIncompleteMissingAttachment: type = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; break; case IGLDevice:: FramebufferIncompleteDrawBuffer: type = "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"; break; case IGLDevice:: FramebufferIncompleteReadBuffer: type = "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"; break; case IGLDevice:: FramebufferIncompleteMultisample: type = "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; break; case IGLDevice:: FramebufferIncompleteLayerTargets: type = "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS"; break; } SPRaise("OpenGL Framebuffer completeness check failed: %s", type.c_str()); } GLFramebufferManager::GLFramebufferManager(IGLDevice *dev): device(dev){ SPADES_MARK_FUNCTION(); SPLog("Initializing framebuffer manager"); if((!r_blitFramebuffer) && r_multisamples) { SPLog("WARNING: Disabling MSAA: no support for MSAA when r_blitFramebuffer = 0"); r_multisamples = 0; } useMultisample = (int)r_multisamples > 0; useHighPrec = r_highPrec ? 1 : 0; if(useMultisample){ SPLog("Multi-sample Antialiasing Enabled"); // for multisample rendering, use // multisample renderbuffer for scene // rendering. multisampledFramebuffer = dev->GenFramebuffer(); dev->BindFramebuffer(IGLDevice::Framebuffer, multisampledFramebuffer); multisampledDepthRenderbuffer = dev->GenRenderbuffer(); dev->BindRenderbuffer(IGLDevice::Renderbuffer, multisampledDepthRenderbuffer); dev->RenderbufferStorage(IGLDevice::Renderbuffer, (int)r_multisamples, IGLDevice::DepthComponent24, dev->ScreenWidth(), dev->ScreenHeight()); SPLog("MSAA Depth Buffer Allocated"); dev->FramebufferRenderbuffer(IGLDevice::Framebuffer, IGLDevice::DepthAttachment, IGLDevice::Renderbuffer, multisampledDepthRenderbuffer); multisampledColorRenderbuffer = dev->GenRenderbuffer(); dev->BindRenderbuffer(IGLDevice::Renderbuffer, multisampledColorRenderbuffer); if(r_srgb){ SPLog("Creating MSAA Color Buffer with SRGB8_ALPHA"); useHighPrec = false; dev->RenderbufferStorage(IGLDevice::Renderbuffer, (int)r_multisamples, IGLDevice::SRGB8Alpha, dev->ScreenWidth(), dev->ScreenHeight()); SPLog("MSAA Color Buffer Allocated"); dev->FramebufferRenderbuffer(IGLDevice::Framebuffer, IGLDevice::ColorAttachment0, IGLDevice::Renderbuffer, multisampledColorRenderbuffer); IGLDevice::Enum status = dev->CheckFramebufferStatus(IGLDevice::Framebuffer); if(status != IGLDevice::FramebufferComplete) { RaiseFBStatusError(status); } fbInternalFormat = IGLDevice::SRGB8Alpha; SPLog("MSAA Framebuffer Allocated"); }else{ try{ if(!useHighPrec){ SPLog("RGB10A2 disabled"); SPRaise("jump to catch(...)"); } dev->RenderbufferStorage(IGLDevice::Renderbuffer, (int)r_multisamples, IGLDevice::RGB10A2, dev->ScreenWidth(), dev->ScreenHeight()); SPLog("MSAA Color Buffer Allocated"); dev->FramebufferRenderbuffer(IGLDevice::Framebuffer, IGLDevice::ColorAttachment0, IGLDevice::Renderbuffer, multisampledColorRenderbuffer); IGLDevice::Enum status = dev->CheckFramebufferStatus(IGLDevice::Framebuffer); if(status != IGLDevice::FramebufferComplete) { RaiseFBStatusError(status); } fbInternalFormat = IGLDevice::RGB10A2; SPLog("MSAA Framebuffer Allocated"); }catch(...){ SPLog("Renderbuffer creation failed: trying with RGB8A8"); useHighPrec = false; dev->RenderbufferStorage(IGLDevice::Renderbuffer, (int)r_multisamples, IGLDevice::RGBA8, dev->ScreenWidth(), dev->ScreenHeight()); SPLog("MSAA Color Buffer Allocated"); dev->FramebufferRenderbuffer(IGLDevice::Framebuffer, IGLDevice::ColorAttachment0, IGLDevice::Renderbuffer, multisampledColorRenderbuffer); IGLDevice::Enum status = dev->CheckFramebufferStatus(IGLDevice::Framebuffer); if(status != IGLDevice::FramebufferComplete) { RaiseFBStatusError(status); } fbInternalFormat = IGLDevice::RGBA8; SPLog("MSAA Framebuffer Allocated"); } } } SPLog("Creating Non-MSAA Buffer"); // in non-multisampled rendering, // we can directly draw into // texture. // in multisampled rendering, // we must first copy to non-multismapled // framebuffer to use it in shader as a texture. renderFramebuffer = dev->GenFramebuffer(); dev->BindFramebuffer(IGLDevice::Framebuffer, renderFramebuffer); renderDepthTexture = dev->GenTexture(); dev->BindTexture(IGLDevice::Texture2D, renderDepthTexture); dev->TexImage2D(IGLDevice::Texture2D, 0, IGLDevice::DepthComponent24, dev->ScreenWidth(), dev->ScreenHeight(), 0, IGLDevice::DepthComponent, IGLDevice::UnsignedInt, NULL); SPLog("Depth Buffer Allocated"); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter, IGLDevice::Nearest); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, IGLDevice::Nearest); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapS, IGLDevice::ClampToEdge); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapT, IGLDevice::ClampToEdge); dev->FramebufferTexture2D(IGLDevice::Framebuffer, IGLDevice::DepthAttachment, IGLDevice::Texture2D, renderDepthTexture, 0); renderColorTexture = dev->GenTexture(); dev->BindTexture(IGLDevice::Texture2D, renderColorTexture); if(r_srgb){ SPLog("Creating Non-MSAA SRGB buffer"); useHighPrec = false; dev->TexImage2D(IGLDevice::Texture2D, 0, IGLDevice::SRGB8Alpha, dev->ScreenWidth(), dev->ScreenHeight(), 0, IGLDevice::RGBA, IGLDevice::UnsignedByte, NULL); SPLog("Color Buffer Allocated"); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter, IGLDevice::Linear); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, IGLDevice::Linear); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapS, IGLDevice::ClampToEdge); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapT, IGLDevice::ClampToEdge); dev->FramebufferTexture2D(IGLDevice::Framebuffer, IGLDevice::ColorAttachment0, IGLDevice::Texture2D, renderColorTexture, 0); IGLDevice::Enum status = dev->CheckFramebufferStatus(IGLDevice::Framebuffer); if(status != IGLDevice::FramebufferComplete) { RaiseFBStatusError(status); } fbInternalFormat = IGLDevice::SRGB8Alpha; SPLog("Framebuffer Created"); }else{ try{ if(!useHighPrec){ SPLog("RGB10A2 disabled"); SPRaise("jump to catch(...)"); } dev->TexImage2D(IGLDevice::Texture2D, 0, IGLDevice::RGB10A2, dev->ScreenWidth(), dev->ScreenHeight(), 0, IGLDevice::RGBA, IGLDevice::UnsignedByte, NULL); SPLog("Color Buffer Allocated"); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter, IGLDevice::Linear); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, IGLDevice::Linear); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapS, IGLDevice::ClampToEdge); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapT, IGLDevice::ClampToEdge); dev->FramebufferTexture2D(IGLDevice::Framebuffer, IGLDevice::ColorAttachment0, IGLDevice::Texture2D, renderColorTexture, 0); IGLDevice::Enum status = dev->CheckFramebufferStatus(IGLDevice::Framebuffer); if(status != IGLDevice::FramebufferComplete) { RaiseFBStatusError(status); } fbInternalFormat = IGLDevice::RGB10A2; SPLog("Framebuffer Created"); }catch(...){ SPLog("Texture creation failed: trying with RGB8A8"); useHighPrec = false; dev->TexImage2D(IGLDevice::Texture2D, 0, IGLDevice::RGBA8, dev->ScreenWidth(), dev->ScreenHeight(), 0, IGLDevice::RGBA, IGLDevice::UnsignedByte, NULL); SPLog("Color Buffer Allocated"); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter, IGLDevice::Linear); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, IGLDevice::Linear); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapS, IGLDevice::ClampToEdge); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapT, IGLDevice::ClampToEdge); dev->FramebufferTexture2D(IGLDevice::Framebuffer, IGLDevice::ColorAttachment0, IGLDevice::Texture2D, renderColorTexture, 0); IGLDevice::Enum status = dev->CheckFramebufferStatus(IGLDevice::Framebuffer); if(status != IGLDevice::FramebufferComplete) { RaiseFBStatusError(status); } fbInternalFormat = IGLDevice::RGBA8; SPLog("Framebuffer Created"); } } if((int)r_water >= 2) { SPLog("Creating Mirror framebuffer"); mirrorFramebuffer = dev->GenFramebuffer(); dev->BindFramebuffer(IGLDevice::Framebuffer, mirrorFramebuffer); mirrorColorTexture = dev->GenTexture(); dev->BindTexture(IGLDevice::Texture2D, mirrorColorTexture); SPLog("Creating Mirror texture"); dev->TexImage2D(IGLDevice::Texture2D, 0, fbInternalFormat, dev->ScreenWidth(), dev->ScreenHeight(), 0, IGLDevice::RGBA, IGLDevice::UnsignedByte, NULL); SPLog("Color Buffer Allocated"); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter, IGLDevice::Linear); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, IGLDevice::Linear); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapS, IGLDevice::ClampToEdge); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapT, IGLDevice::ClampToEdge); dev->FramebufferTexture2D(IGLDevice::Framebuffer, IGLDevice::ColorAttachment0, IGLDevice::Texture2D, mirrorColorTexture, 0); IGLDevice::Enum status = dev->CheckFramebufferStatus(IGLDevice::Framebuffer); if(status != IGLDevice::FramebufferComplete) { RaiseFBStatusError(status); } SPLog("Mirror Framebuffer Created"); } // (int)r_water >= 2 // add render buffer as a registered buffer Buffer buf; buf.framebuffer = renderFramebuffer; buf.texture = renderColorTexture; buf.refCount = 0; buf.w = device->ScreenWidth(); buf.h = device->ScreenHeight(); buf.internalFormat = fbInternalFormat; buffers.push_back(buf); dev->BindFramebuffer(IGLDevice::Framebuffer, 0); dev->BindRenderbuffer(IGLDevice::Renderbuffer, 0); } GLFramebufferManager::~GLFramebufferManager(){ // maybe framebuffers are released automatically when // application quits... } void GLFramebufferManager::PrepareSceneRendering() { SPADES_MARK_FUNCTION(); if(useMultisample){ // ---- multisampled device->BindFramebuffer(IGLDevice::Framebuffer, multisampledFramebuffer); device->Enable(IGLDevice::Multisample, useMultisample); }else { // ---- single sampled device->BindFramebuffer(IGLDevice::Framebuffer, renderFramebuffer); // calling glDisable(GL_MULTISAMPLE) on non-MSAA FB // causes GL_INVALID_FRAMEBUFFER_OPERATION on // some video drivers? } device->Enable(IGLDevice::DepthTest, true); device->DepthMask(true); } GLColorBuffer GLFramebufferManager::PrepareForWaterRendering(IGLDevice::UInteger tempFb, IGLDevice::UInteger tempDepthTex) { SPADES_MARK_FUNCTION(); BufferHandle handle; if(useMultisample){ handle = BufferHandle(this, 0); }else{ // don't want to the renderBuffer to be returned BufferHandle captured = BufferHandle(this, 0); handle = CreateBufferHandle(-1, -1, true); captured.Release(); } device->BindFramebuffer(IGLDevice::Framebuffer, tempFb); device->FramebufferTexture2D(IGLDevice::Framebuffer, IGLDevice::ColorAttachment0, IGLDevice::Texture2D, handle.GetTexture(), 0); // downsample int w = device->ScreenWidth(); int h = device->ScreenHeight(); if(r_blitFramebuffer){ if(useMultisample){ device->BindFramebuffer(IGLDevice::ReadFramebuffer, multisampledFramebuffer); }else{ device->BindFramebuffer(IGLDevice::ReadFramebuffer, renderFramebuffer); } device->BindFramebuffer(IGLDevice::DrawFramebuffer, tempFb); device->BlitFramebuffer(0, 0, w, h, 0, 0, w, h, IGLDevice::ColorBufferBit, IGLDevice::Nearest); device->BlitFramebuffer(0, 0, w, h, 0, 0, w, h, IGLDevice::DepthBufferBit, IGLDevice::Nearest); device->BindFramebuffer(IGLDevice::ReadFramebuffer, 0); device->BindFramebuffer(IGLDevice::DrawFramebuffer, 0); }else{ if(useMultisample){ device->BindFramebuffer(IGLDevice::Framebuffer, multisampledFramebuffer); }else{ device->BindFramebuffer(IGLDevice::Framebuffer, renderFramebuffer); } device->BindTexture(IGLDevice::Texture2D, handle.GetTexture()); device->CopyTexSubImage2D(IGLDevice::Texture2D, 0, 0, 0, 0, 0, w, h); device->BindTexture(IGLDevice::Texture2D, tempDepthTex); device->CopyTexSubImage2D(IGLDevice::Texture2D, 0, 0, 0, 0, 0, w, h); } // restore render framebuffer if(useMultisample){ // ---- multisampled device->BindFramebuffer(IGLDevice::Framebuffer, multisampledFramebuffer); }else { // ---- single sampled device->BindFramebuffer(IGLDevice::Framebuffer, renderFramebuffer); } return handle; } void GLFramebufferManager::ClearMirrorTexture(spades::Vector3 bgCol) { device->BindFramebuffer(IGLDevice::Framebuffer, mirrorFramebuffer); device->Viewport(0, 0, device->ScreenWidth(), device->ScreenHeight()); device->ClearColor(bgCol.x, bgCol.y, bgCol.z, 1.f); device->Clear((IGLDevice::Enum)(IGLDevice::ColorBufferBit | IGLDevice::DepthBufferBit)); // restore framebuffer if(useMultisample){ // ---- multisampled device->BindFramebuffer(IGLDevice::Framebuffer, multisampledFramebuffer); }else { // ---- single sampled device->BindFramebuffer(IGLDevice::Framebuffer, renderFramebuffer); } } void GLFramebufferManager::CopyToMirrorTexture(IGLDevice::UInteger fb) { SPADES_MARK_FUNCTION(); int w = device->ScreenWidth(); int h = device->ScreenHeight(); if(fb == 0){ fb = useMultisample ? multisampledFramebuffer : renderFramebuffer; } if(useMultisample){ // downsample if(r_blitFramebuffer){ device->BindFramebuffer(IGLDevice::ReadFramebuffer, fb); device->BindFramebuffer(IGLDevice::DrawFramebuffer, mirrorFramebuffer); device->BlitFramebuffer(0, 0, w, h, 0, 0, w, h, IGLDevice::ColorBufferBit, IGLDevice::Nearest); device->BindFramebuffer(IGLDevice::ReadFramebuffer, 0); device->BindFramebuffer(IGLDevice::DrawFramebuffer, 0); }else{ device->BindFramebuffer(IGLDevice::Framebuffer, fb); device->BindTexture(IGLDevice::Texture2D, mirrorColorTexture); device->CopyTexSubImage2D(IGLDevice::Texture2D, 0, 0, 0, 0, 0, w, h); } }else{ // copy if(r_blitFramebuffer){ device->BindFramebuffer(IGLDevice::ReadFramebuffer, fb); device->BindFramebuffer(IGLDevice::DrawFramebuffer, mirrorFramebuffer); device->BlitFramebuffer(0, 0, w, h, 0, 0, w, h, IGLDevice::ColorBufferBit, IGLDevice::Nearest); device->BindFramebuffer(IGLDevice::ReadFramebuffer, 0); device->BindFramebuffer(IGLDevice::DrawFramebuffer, 0); }else{ device->BindFramebuffer(IGLDevice::Framebuffer, fb); device->BindTexture(IGLDevice::Texture2D, mirrorColorTexture); device->CopyTexSubImage2D(IGLDevice::Texture2D, 0, 0, 0, 0, 0, w, h); } } device->BindTexture(IGLDevice::Texture2D, mirrorColorTexture); //device->GenerateMipmap(IGLDevice::Texture2D); // restore framebuffer if(useMultisample){ // ---- multisampled device->BindFramebuffer(IGLDevice::Framebuffer, multisampledFramebuffer); }else { // ---- single sampled device->BindFramebuffer(IGLDevice::Framebuffer, renderFramebuffer); } device->Enable(IGLDevice::DepthTest, true); device->DepthMask(true); } GLFramebufferManager::BufferHandle GLFramebufferManager::StartPostProcessing() { SPADES_MARK_FUNCTION(); if(useMultisample){ // downsample int w = device->ScreenWidth(); int h = device->ScreenHeight(); if(r_blitFramebuffer){ device->BindFramebuffer(IGLDevice::ReadFramebuffer, multisampledFramebuffer); device->BindFramebuffer(IGLDevice::DrawFramebuffer, renderFramebuffer); device->BlitFramebuffer(0, 0, w, h, 0, 0, w, h, IGLDevice::ColorBufferBit, IGLDevice::Nearest); device->BlitFramebuffer(0, 0, w, h, 0, 0, w, h, IGLDevice::DepthBufferBit, IGLDevice::Nearest); device->BindFramebuffer(IGLDevice::ReadFramebuffer, 0); device->BindFramebuffer(IGLDevice::DrawFramebuffer, 0); }else{ device->BindFramebuffer(IGLDevice::Framebuffer, multisampledFramebuffer); device->BindTexture(IGLDevice::Texture2D, renderColorTexture); device->CopyTexSubImage2D(IGLDevice::Texture2D, 0, 0, 0, 0, 0, w, h); device->BindTexture(IGLDevice::Texture2D, renderDepthTexture); device->CopyTexSubImage2D(IGLDevice::Texture2D, 0, 0, 0, 0, 0, w, h); } } device->Enable(IGLDevice::DepthTest, false); device->DepthMask(false); // zero is always renderFramebuffer return BufferHandle(this, 0); } void GLFramebufferManager::MakeSureAllBuffersReleased(){ SPADES_MARK_FUNCTION(); for(size_t i = 0; i < buffers.size(); i++){ SPAssert(buffers[i].refCount == 0); } } GLFramebufferManager::BufferHandle GLFramebufferManager::CreateBufferHandle(int w, int h, bool alpha){ IGLDevice::Enum ifmt; if(alpha){ if(r_srgb) ifmt = IGLDevice::SRGB8Alpha; else ifmt = IGLDevice::RGBA8; }else{ ifmt = fbInternalFormat; } return CreateBufferHandle(w, h, ifmt); } GLFramebufferManager::BufferHandle GLFramebufferManager::CreateBufferHandle(int w, int h, IGLDevice::Enum iFormat) { SPADES_MARK_FUNCTION(); if(w < 0) w = device->ScreenWidth(); if(h < 0) h = device->ScreenHeight(); for(size_t i = 0; i < buffers.size(); i++){ Buffer& b = buffers[i]; if(b.refCount > 0) continue; if(b.w != w || b.h != h) continue; if(b.internalFormat != iFormat) continue; return BufferHandle(this, i); } if(buffers.size() > 128){ SPRaise("Maximum number of framebuffers exceeded"); } SPLog("New GLColorBuffer requested (w = %d, h = %d, ifmt = 0x%04x)", w, h, (int)iFormat); // no buffer is free! IGLDevice::Enum ifmt = iFormat; IGLDevice::UInteger tex = device->GenTexture(); device->BindTexture(IGLDevice::Texture2D, tex); device->TexImage2D(IGLDevice::Texture2D, 0, ifmt, w, h, 0, IGLDevice::Red, IGLDevice::UnsignedByte, NULL); SPLog("Texture allocated."); device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter, IGLDevice::Linear); device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, IGLDevice::Linear); device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapS, IGLDevice::ClampToEdge); device->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapT, IGLDevice::ClampToEdge); IGLDevice::UInteger fb = device->GenFramebuffer(); device->BindFramebuffer(IGLDevice::Framebuffer, fb); device->FramebufferTexture2D(IGLDevice::Framebuffer, IGLDevice::ColorAttachment0, IGLDevice::Texture2D, tex, 0); SPLog("Framebuffer created."); device->BindFramebuffer(IGLDevice::Framebuffer, 0); Buffer buf; buf.framebuffer = fb; buf.texture = tex; buf.refCount = 0; buf.w = w; buf.h = h; buf.internalFormat = ifmt; buffers.push_back(buf); return BufferHandle(this, buffers.size() - 1); } #pragma mark - BufferHandle GLFramebufferManager::BufferHandle::BufferHandle(): manager(NULL), bufferIndex(0), valid(false){ } GLFramebufferManager::BufferHandle::BufferHandle(GLFramebufferManager*m, size_t index): manager(m), bufferIndex(index), valid(true){ SPAssert(bufferIndex < manager->buffers.size()); Buffer&b = manager->buffers[bufferIndex]; b.refCount++; } GLFramebufferManager::BufferHandle::BufferHandle(const BufferHandle& other): manager(other.manager), bufferIndex(other.bufferIndex), valid(other.valid){ if(valid){ Buffer&b = manager->buffers[bufferIndex]; b.refCount++; } } GLFramebufferManager::BufferHandle::~BufferHandle(){ Release(); } void GLFramebufferManager::BufferHandle::operator=(const BufferHandle& other){ if(valid){ manager->buffers[bufferIndex].refCount--; } manager = other.manager; bufferIndex = other.bufferIndex; valid = other.valid; if(valid){ manager->buffers[bufferIndex].refCount++; } } void GLFramebufferManager::BufferHandle::Release(){ if(valid){ Buffer&b = manager->buffers[bufferIndex]; SPAssert(b.refCount > 0); b.refCount--; valid = false; } } IGLDevice::UInteger GLFramebufferManager::BufferHandle::GetFramebuffer() { SPAssert(valid); Buffer&b = manager->buffers[bufferIndex]; return b.framebuffer; } IGLDevice::UInteger GLFramebufferManager::BufferHandle::GetTexture() { SPAssert(valid); Buffer&b = manager->buffers[bufferIndex]; return b.texture; } int GLFramebufferManager::BufferHandle::GetWidth() { SPAssert(valid); Buffer&b = manager->buffers[bufferIndex]; return b.w; } int GLFramebufferManager::BufferHandle::GetHeight() { SPAssert(valid); Buffer&b = manager->buffers[bufferIndex]; return b.h; } IGLDevice::Enum GLFramebufferManager::BufferHandle::GetInternalFormat() { SPAssert(valid); Buffer&b = manager->buffers[bufferIndex]; return b.internalFormat; } } }