| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL ES Utilities |
| * ------------------------------------------------ |
| * |
| * Copyright 2014 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. |
| * |
| *//*! |
| * \file |
| * \brief OpenGL ES context wrapper that uses FBO as default framebuffer. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "gluFboRenderContext.hpp" |
| #include "gluContextFactory.hpp" |
| #include "gluRenderConfig.hpp" |
| #include "glwEnums.hpp" |
| #include "glwFunctions.hpp" |
| #include "tcuCommandLine.hpp" |
| #include "gluTextureUtil.hpp" |
| #include "tcuTextureUtil.hpp" |
| |
| #include <sstream> |
| |
| namespace glu |
| { |
| |
| static int getNumDepthBits (const tcu::TextureFormat& format) |
| { |
| if (format.order == tcu::TextureFormat::DS) |
| { |
| const tcu::TextureFormat depthOnlyFormat = tcu::getEffectiveDepthStencilTextureFormat(format, tcu::Sampler::MODE_DEPTH); |
| return tcu::getTextureFormatBitDepth(depthOnlyFormat).x(); |
| } |
| else if (format.order == tcu::TextureFormat::D) |
| return tcu::getTextureFormatBitDepth(format).x(); |
| else |
| return 0; |
| } |
| |
| static int getNumStencilBits (const tcu::TextureFormat& format) |
| { |
| if (format.order == tcu::TextureFormat::DS) |
| { |
| const tcu::TextureFormat stencilOnlyFormat = tcu::getEffectiveDepthStencilTextureFormat(format, tcu::Sampler::MODE_STENCIL); |
| return tcu::getTextureFormatBitDepth(stencilOnlyFormat).x(); |
| } |
| else if (format.order == tcu::TextureFormat::S) |
| return tcu::getTextureFormatBitDepth(format).x(); |
| else |
| return 0; |
| } |
| |
| static tcu::PixelFormat getPixelFormat (deUint32 colorFormat) |
| { |
| const tcu::IVec4 bits = tcu::getTextureFormatBitDepth(glu::mapGLInternalFormat(colorFormat)); |
| return tcu::PixelFormat(bits[0], bits[1], bits[2], bits[3]); |
| } |
| |
| static void getDepthStencilBits (deUint32 depthStencilFormat, int* depthBits, int* stencilBits) |
| { |
| const tcu::TextureFormat combinedFormat = glu::mapGLInternalFormat(depthStencilFormat); |
| |
| *depthBits = getNumDepthBits(combinedFormat); |
| *stencilBits = getNumStencilBits(combinedFormat); |
| } |
| |
| deUint32 chooseColorFormat (const glu::RenderConfig& config) |
| { |
| static const deUint32 s_formats[] = |
| { |
| GL_RGBA8, |
| GL_RGB8, |
| GL_RG8, |
| GL_R8, |
| GL_RGBA4, |
| GL_RGB5_A1, |
| GL_RGB565, |
| GL_RGB5 |
| }; |
| |
| for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(s_formats); fmtNdx++) |
| { |
| const deUint32 format = s_formats[fmtNdx]; |
| const tcu::IVec4 bits = tcu::getTextureFormatBitDepth(glu::mapGLInternalFormat(format)); |
| |
| if (config.redBits != glu::RenderConfig::DONT_CARE && |
| config.redBits != bits[0]) |
| continue; |
| |
| if (config.greenBits != glu::RenderConfig::DONT_CARE && |
| config.greenBits != bits[1]) |
| continue; |
| |
| if (config.blueBits != glu::RenderConfig::DONT_CARE && |
| config.blueBits != bits[2]) |
| continue; |
| |
| if (config.alphaBits != glu::RenderConfig::DONT_CARE && |
| config.alphaBits != bits[3]) |
| continue; |
| |
| return format; |
| } |
| |
| return 0; |
| } |
| |
| deUint32 chooseDepthStencilFormat (const glu::RenderConfig& config) |
| { |
| static const deUint32 s_formats[] = |
| { |
| GL_DEPTH32F_STENCIL8, |
| GL_DEPTH24_STENCIL8, |
| GL_DEPTH_COMPONENT32F, |
| GL_DEPTH_COMPONENT24, |
| GL_DEPTH_COMPONENT16, |
| GL_STENCIL_INDEX8 |
| }; |
| |
| for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(s_formats); fmtNdx++) |
| { |
| const deUint32 format = s_formats[fmtNdx]; |
| const tcu::TextureFormat combinedFormat = glu::mapGLInternalFormat(format); |
| const int depthBits = getNumDepthBits(combinedFormat); |
| const int stencilBits = getNumStencilBits(combinedFormat); |
| |
| if (config.depthBits != glu::RenderConfig::DONT_CARE && |
| config.depthBits != depthBits) |
| continue; |
| |
| if (config.stencilBits != glu::RenderConfig::DONT_CARE && |
| config.stencilBits != stencilBits) |
| continue; |
| |
| return format; |
| } |
| |
| return 0; |
| } |
| |
| FboRenderContext::FboRenderContext (RenderContext* context, const RenderConfig& config) |
| : m_context (context) |
| , m_framebuffer (0) |
| , m_colorBuffer (0) |
| , m_depthStencilBuffer (0) |
| , m_renderTarget () |
| { |
| try |
| { |
| createFramebuffer(config); |
| } |
| catch (...) |
| { |
| destroyFramebuffer(); |
| throw; |
| } |
| } |
| |
| FboRenderContext::FboRenderContext (const ContextFactory& factory, const RenderConfig& config, const tcu::CommandLine& cmdLine) |
| : m_context (DE_NULL) |
| , m_framebuffer (0) |
| , m_colorBuffer (0) |
| , m_depthStencilBuffer (0) |
| , m_renderTarget () |
| { |
| try |
| { |
| RenderConfig nativeRenderConfig; |
| nativeRenderConfig.type = config.type; |
| nativeRenderConfig.windowVisibility = config.windowVisibility; |
| // \note All other properties are defaults, mostly DONT_CARE |
| m_context = factory.createContext(nativeRenderConfig, cmdLine, DE_NULL); |
| createFramebuffer(config); |
| } |
| catch (...) |
| { |
| delete m_context; |
| throw; |
| } |
| } |
| |
| FboRenderContext::~FboRenderContext (void) |
| { |
| // \todo [2013-04-08 pyry] Do we want to destry FBO before destroying context? |
| delete m_context; |
| } |
| |
| void FboRenderContext::postIterate (void) |
| { |
| // \todo [2012-11-27 pyry] Blit to default framebuffer in ES3? |
| m_context->getFunctions().finish(); |
| } |
| |
| void FboRenderContext::makeCurrent(void) |
| { |
| m_context->makeCurrent(); |
| } |
| |
| void FboRenderContext::createFramebuffer (const RenderConfig& config) |
| { |
| DE_ASSERT(m_framebuffer == 0 && m_colorBuffer == 0 && m_depthStencilBuffer == 0); |
| |
| const glw::Functions& gl = m_context->getFunctions(); |
| const deUint32 colorFormat = chooseColorFormat(config); |
| const deUint32 depthStencilFormat = chooseDepthStencilFormat(config); |
| int width = config.width; |
| int height = config.height; |
| tcu::PixelFormat pixelFormat; |
| int depthBits = 0; |
| int stencilBits = 0; |
| |
| if (config.numSamples > 0 && !gl.renderbufferStorageMultisample) |
| throw tcu::NotSupportedError("Multisample FBO is not supported"); |
| |
| if (colorFormat == 0) |
| throw tcu::NotSupportedError("Unsupported color attachment format"); |
| |
| if (width == glu::RenderConfig::DONT_CARE || height == glu::RenderConfig::DONT_CARE) |
| { |
| int maxSize = 0; |
| gl.getIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxSize); |
| |
| width = (width == glu::RenderConfig::DONT_CARE) ? maxSize : width; |
| height = (height == glu::RenderConfig::DONT_CARE) ? maxSize : height; |
| } |
| |
| { |
| pixelFormat = getPixelFormat(colorFormat); |
| |
| gl.genRenderbuffers(1, &m_colorBuffer); |
| gl.bindRenderbuffer(GL_RENDERBUFFER, m_colorBuffer); |
| |
| if (config.numSamples > 0) |
| gl.renderbufferStorageMultisample(GL_RENDERBUFFER, config.numSamples, colorFormat, width, height); |
| else |
| gl.renderbufferStorage(GL_RENDERBUFFER, colorFormat, width, height); |
| |
| gl.bindRenderbuffer(GL_RENDERBUFFER, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "Creating color renderbuffer"); |
| } |
| |
| if (depthStencilFormat != GL_NONE) |
| { |
| getDepthStencilBits(depthStencilFormat, &depthBits, &stencilBits); |
| |
| gl.genRenderbuffers(1, &m_depthStencilBuffer); |
| gl.bindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer); |
| |
| if (config.numSamples > 0) |
| gl.renderbufferStorageMultisample(GL_RENDERBUFFER, config.numSamples, depthStencilFormat, width, height); |
| else |
| gl.renderbufferStorage(GL_RENDERBUFFER, depthStencilFormat, width, height); |
| |
| gl.bindRenderbuffer(GL_RENDERBUFFER, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "Creating depth / stencil renderbuffer"); |
| } |
| |
| gl.genFramebuffers(1, &m_framebuffer); |
| gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); |
| |
| if (m_colorBuffer) |
| gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_colorBuffer); |
| |
| if (m_depthStencilBuffer) |
| { |
| if (depthBits > 0) |
| gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer); |
| |
| if (stencilBits > 0) |
| gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer); |
| } |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "Creating framebuffer"); |
| |
| if (gl.checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) |
| throw tcu::NotSupportedError("Framebuffer is not complete"); |
| |
| // Set up correct viewport for first test case. |
| gl.viewport(0, 0, width, height); |
| |
| m_renderTarget = tcu::RenderTarget(width, height, pixelFormat, depthBits, stencilBits, config.numSamples); |
| } |
| |
| void FboRenderContext::destroyFramebuffer (void) |
| { |
| const glw::Functions& gl = m_context->getFunctions(); |
| |
| if (m_framebuffer) |
| { |
| gl.deleteFramebuffers(1, &m_framebuffer); |
| m_framebuffer = 0; |
| } |
| |
| if (m_depthStencilBuffer) |
| { |
| gl.deleteRenderbuffers(1, &m_depthStencilBuffer); |
| m_depthStencilBuffer = 0; |
| } |
| |
| if (m_colorBuffer) |
| { |
| gl.deleteRenderbuffers(1, &m_colorBuffer); |
| m_colorBuffer = 0; |
| } |
| } |
| |
| } // glu |