PixelBox GL3PlusHardwarePixelBuffer::lockImpl( const Image::Box &lockBox, LockOptions options ) { //Allocate memory for the entire image, as the buffer //maynot be freed and be reused in subsequent calls. allocateBuffer( PixelUtil::getMemorySize( mWidth, mHeight, mDepth, mFormat ) ); mBuffer = PixelBox( lockBox.getWidth(), lockBox.getHeight(), lockBox.getDepth(), mFormat, mBuffer.data ); mCurrentLock = mBuffer; mCurrentLock.left = lockBox.left; mCurrentLock.right += lockBox.left; mCurrentLock.top = lockBox.top; mCurrentLock.bottom += lockBox.top; if(options != HardwareBuffer::HBL_DISCARD) { // Download the old contents of the texture download( mCurrentLock ); } mCurrentLockOptions = options; mLockedBox = lockBox; mCurrentLock = mBuffer; return mBuffer; }
void GLES2HardwarePixelBuffer::blitFromMemory(const PixelBox &src, const Image::Box &dstBox) { if (!mBuffer.contains(dstBox)) { OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Destination box out of range", "GLES2HardwarePixelBuffer::blitFromMemory"); } PixelBox scaled; if (src.getWidth() != dstBox.getWidth() || src.getHeight() != dstBox.getHeight() || src.getDepth() != dstBox.getDepth()) { // Scale to destination size. // This also does pixel format conversion if needed allocateBuffer(); scaled = mBuffer.getSubVolume(dstBox); Image::scale(src, scaled, Image::FILTER_BILINEAR); } #if OGRE_PLATFORM != OGRE_PLATFORM_APPLE_IOS else if ((src.format != mFormat) || ((GLES2PixelUtil::getGLOriginFormat(src.format) == 0) && (src.format != PF_R8G8B8))) #else else if (GLES2PixelUtil::getGLOriginFormat(src.format) == 0) #endif { // Extents match, but format is not accepted as valid source format for GL // do conversion in temporary buffer allocateBuffer(); scaled = mBuffer.getSubVolume(dstBox); PixelUtil::bulkPixelConversion(src, scaled); } else { allocateBuffer(); // No scaling or conversion needed scaled = src; if (src.format == PF_R8G8B8) { scaled.format = PF_B8G8R8; PixelUtil::bulkPixelConversion(src, scaled); } #if OGRE_PLATFORM == OGRE_PLATFORM_NACL if (src.format == PF_A8R8G8B8) { scaled.format = PF_A8B8G8R8; PixelUtil::bulkPixelConversion(src, scaled); } #endif } upload(scaled, dstBox); freeBuffer(); }
void GLESHardwarePixelBuffer::blitToMemory(const Image::Box &srcBox, const PixelBox &dst) { if (!mBuffer.contains(srcBox)) { OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "source box out of range", "GLESHardwarePixelBuffer::blitToMemory"); } if (srcBox.left == 0 && srcBox.right == getWidth() && srcBox.top == 0 && srcBox.bottom == getHeight() && srcBox.front == 0 && srcBox.back == getDepth() && dst.getWidth() == getWidth() && dst.getHeight() == getHeight() && dst.getDepth() == getDepth() && GLESPixelUtil::getGLOriginFormat(dst.format) != 0) { // The direct case: the user wants the entire texture in a format supported by GL // so we don't need an intermediate buffer download(dst); } else { // Use buffer for intermediate copy allocateBuffer(); // Download entire buffer download(mBuffer); if(srcBox.getWidth() != dst.getWidth() || srcBox.getHeight() != dst.getHeight() || srcBox.getDepth() != dst.getDepth()) { // We need scaling Image::scale(mBuffer.getSubVolume(srcBox), dst, Image::FILTER_BILINEAR); } else { // Just copy the bit that we need PixelUtil::bulkPixelConversion(mBuffer.getSubVolume(srcBox), dst); } freeBuffer(); } }
void GL3PlusHardwarePixelBuffer::blitFromMemory(const PixelBox &src, const Image::Box &dstBox) { if (!mBuffer.contains(dstBox)) { OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Destination box out of range", "GL3PlusHardwarePixelBuffer::blitFromMemory"); } PixelBox scaled; if (src.getWidth() != dstBox.getWidth() || src.getHeight() != dstBox.getHeight() || src.getDepth() != dstBox.getDepth()) { // Scale to destination size. // This also does pixel format conversion if needed. allocateBuffer( mSizeInBytes ); scaled = mBuffer.getSubVolume(dstBox); Image::scale(src, scaled, Image::FILTER_BILINEAR); } else if (GL3PlusPixelUtil::getGLOriginFormat(src.format) == 0) { // Extents match, but format is not accepted as valid // source format for GL. Do conversion in temporary buffer. allocateBuffer( mSizeInBytes ); scaled = mBuffer.getSubVolume(dstBox); PixelUtil::bulkPixelConversion(src, scaled); } else { allocateBuffer( mSizeInBytes ); // No scaling or conversion needed. scaled = src; } upload(scaled, dstBox); freeBuffer(); }
void GLESTextureBuffer::upload(const PixelBox &data, const Image::Box &dest) { glBindTexture(mTarget, mTextureID); GL_CHECK_ERROR; if (PixelUtil::isCompressed(data.format)) { if(data.format != mFormat || !data.isConsecutive()) OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Compressed images must be consecutive, in the source format", "GLESTextureBuffer::upload"); GLenum format = GLESPixelUtil::getClosestGLInternalFormat(mFormat); // Data must be consecutive and at beginning of buffer as PixelStorei not allowed // for compressed formats if (dest.left == 0 && dest.top == 0) { glCompressedTexImage2D(mFaceTarget, mLevel, format, dest.getWidth(), dest.getHeight(), 0, data.getConsecutiveSize(), data.data); GL_CHECK_ERROR; } else { glCompressedTexSubImage2D(mFaceTarget, mLevel, dest.left, dest.top, dest.getWidth(), dest.getHeight(), format, data.getConsecutiveSize(), data.data); GL_CHECK_ERROR; } } else if (mSoftwareMipmap) { if (data.getWidth() != data.rowPitch) { // TODO OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLESTextureBuffer::upload"); } if (data.getHeight() * data.getWidth() != data.slicePitch) { // TODO OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLESTextureBuffer::upload"); } glPixelStorei(GL_UNPACK_ALIGNMENT, 1); GL_CHECK_ERROR; buildMipmaps(data); } else { if(data.getWidth() != data.rowPitch) { // TODO OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLESTextureBuffer::upload"); } if(data.getHeight()*data.getWidth() != data.slicePitch) { // TODO OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLESTextureBuffer::upload"); } if ((data.getWidth() * PixelUtil::getNumElemBytes(data.format)) & 3) { // Standard alignment of 4 is not right glPixelStorei(GL_UNPACK_ALIGNMENT, 1); GL_CHECK_ERROR; } // LogManager::getSingleton().logMessage("GLESTextureBuffer::upload - ID: " + StringConverter::toString(mTextureID) + // " Format: " + PixelUtil::getFormatName(data.format) + // " Origin format: " + StringConverter::toString(GLESPixelUtil::getGLOriginFormat(data.format, 0, ' ', std::ios::hex)) + // " Data type: " + StringConverter::toString(GLESPixelUtil::getGLOriginDataType(data.format, 0, ' ', std::ios::hex)) // ); glTexSubImage2D(mFaceTarget, mLevel, dest.left, dest.top, dest.getWidth(), dest.getHeight(), GLESPixelUtil::getGLOriginFormat(data.format), GLESPixelUtil::getGLOriginDataType(data.format), data.data); GL_CHECK_ERROR; } glPixelStorei(GL_UNPACK_ALIGNMENT, 4); GL_CHECK_ERROR; }
//----------------------------------------------------------------------- void PagingLandScapeTexture_InstantBaseTextureEdit::update() { assert (!mMaterial.isNull() && "PagingLandScapeTexture_InstantBaseTextureEdit::update()"); assert (!mTexture.isNull() && "PagingLandScapeTexture_InstantBaseTextureEdit::update()"); assert (!mBuffer.isNull() && "PagingLandScapeTexture_InstantBaseTextureEdit::update()"); // at least deformed once, so need to save texture if asked by user (option) mIsModified = true; Image::Box rect (0, 0, 0, 0, 0, 1); // computes deformation PagingLandScapeData2D *data; const bool isDeformed = mIsDeformRectModified; if (isDeformed) { rect = mDeformRect; data = PagingLandScapeData2DManager::getSingleton().getData2d(mDataX, mDataZ); // rect = data->getDeformationRectangle (); if (rect.getWidth() && rect.getHeight ()) { rect.right += 1; rect.bottom += 1; // Do here some alpha map modification based on deformation computeInstantBase(data, rect); } } // try to upload only the smallest rectangle containing modification if (mIsPaintRectModified) { if (isDeformed) { rect.left = std::min (mPaintRect.left, rect.left); rect.right = std::max (mPaintRect.right, rect.right); rect.top = std::min (mPaintRect.top, rect.top); rect.bottom = std::max (mPaintRect.bottom, rect.bottom); } // if (mNeedUpdate) else { rect = mPaintRect; rect.right += 1; rect.bottom += 1; } } // if (mIsRectModified) // Upload any changes (deformation or ) if (rect.getWidth() && rect.getHeight ()) { const PixelBox srcBox = mImage.getPixelBox().getSubVolume(rect); const PixelBox lock = mBuffer->lock(rect, HardwareBuffer::HBL_DISCARD); PixelUtil::bulkPixelConversion(srcBox, lock); mBuffer->unlock(); } // if (rect.getWidth() && rect.getHeight ()) if (isDeformed) data->resetDeformationRectangle (); PagingLandScapeTexture::updated (); }
//----------------------------------------------------------------------------- PixelBox D3D10HardwarePixelBuffer::lockImpl(const Image::Box lockBox, LockOptions options) { // Check for misuse if(mUsage & TU_RENDERTARGET) OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "DirectX does not allow locking of or directly writing to RenderTargets. Use blitFromMemory if you need the contents.", "D3D10HardwarePixelBuffer::lockImpl"); // Set extents and format // Note that we do not carry over the left/top/front here, since the returned // PixelBox will be re-based from the locking point onwards PixelBox rval(lockBox.getWidth(), lockBox.getHeight(), lockBox.getDepth(), mFormat); // Set locking flags according to options D3D10_MAP flags = D3D10_MAP_WRITE_DISCARD ; switch(options) { case HBL_DISCARD: // D3D only likes D3DLOCK_DISCARD if you created the texture with D3DUSAGE_DYNAMIC // debug runtime flags this up, could cause problems on some drivers if (mUsage & HBU_DYNAMIC) flags = D3D10_MAP_WRITE_DISCARD; break; case HBL_READ_ONLY: flags = D3D10_MAP_READ; break; default: break; }; mDevice.clearStoredErrorMessages(); // TODO - check return values here switch(mParentTexture->getTextureType()) { case TEX_TYPE_1D: { mParentTexture->GetTex1D()->Map(static_cast<UINT>(mSubresourceIndex), flags, 0, &rval.data); if (mDevice.isError()) { String errorDescription = mDevice.getErrorDescription(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3D10 device cannot map 1D texture\nError Description:" + errorDescription, "D3D10HardwarePixelBuffer::lockImpl"); } } break; case TEX_TYPE_CUBE_MAP: case TEX_TYPE_2D: { D3D10_MAPPED_TEXTURE2D mappedTex2D; mParentTexture->GetTex2D()->Map(static_cast<UINT>(mSubresourceIndex), flags, 0, &mappedTex2D); rval.data = mappedTex2D.pData; if (mDevice.isError()) { String errorDescription = mDevice.getErrorDescription(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3D10 device cannot map 1D texture\nError Description:" + errorDescription, "D3D10HardwarePixelBuffer::lockImpl"); } } break; case TEX_TYPE_3D: { D3D10_MAPPED_TEXTURE3D mappedTex3D; mParentTexture->GetTex3D()->Map(static_cast<UINT>(mSubresourceIndex), flags, 0, &mappedTex3D); rval.data = mappedTex3D.pData; if (mDevice.isError()) { String errorDescription = mDevice.getErrorDescription(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3D10 device cannot map 1D texture\nError Description:" + errorDescription, "D3D10HardwarePixelBuffer::lockImpl"); } } break; } return rval; }
//----------------------------------------------------------------------------- void D3D10HardwarePixelBuffer::blitFromMemory(const PixelBox &src, const Image::Box &dstBox) { bool isDds = false; switch(mFormat) { case PF_DXT1: case PF_DXT2: case PF_DXT3: case PF_DXT4: case PF_DXT5: isDds = true; break; default: break; } if (isDds && (dstBox.getWidth() % 4 != 0 || dstBox.getHeight() % 4 != 0 )) { return; } // for scoped deletion of conversion buffer MemoryDataStreamPtr buf; PixelBox converted = src; D3D10_BOX dstBoxDx10 = OgreImageBoxToDx10Box(dstBox); // convert to pixelbuffer's native format if necessary if (src.format != mFormat) { buf.bind(new MemoryDataStream( PixelUtil::getMemorySize(src.getWidth(), src.getHeight(), src.getDepth(), mFormat))); converted = PixelBox(src.getWidth(), src.getHeight(), src.getDepth(), mFormat, buf->getPtr()); PixelUtil::bulkPixelConversion(src, converted); } // In d3d10 the Row Pitch is defined as: "The size of one row of the source data" and not // the same as the OGRE row pitch - meaning that we need to multiple the OGRE row pitch // with the size in bytes of the element to get the d3d10 row pitch. UINT d3dRowPitch = static_cast<UINT>(converted.rowPitch) * static_cast<UINT>(PixelUtil::getNumElemBytes(mFormat)); switch(mParentTexture->getTextureType()) { case TEX_TYPE_1D: { mDevice->UpdateSubresource( mParentTexture->GetTex1D(), 0, &dstBoxDx10, converted.data, 0, 0 ); if (mDevice.isError()) { String errorDescription = mDevice.getErrorDescription(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3D10 device cannot update 1d subresource\nError Description:" + errorDescription, "D3D10HardwarePixelBuffer::blitFromMemory"); } } break; case TEX_TYPE_CUBE_MAP: case TEX_TYPE_2D: { mDevice->UpdateSubresource( mParentTexture->GetTex2D(), static_cast<UINT>(mSubresourceIndex), &dstBoxDx10, converted.data, d3dRowPitch, mFace ); if (mDevice.isError()) { String errorDescription = mDevice.getErrorDescription(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3D10 device cannot update 2d subresource\nError Description:" + errorDescription, "D3D10HardwarePixelBuffer::blitFromMemory"); } } break; case TEX_TYPE_3D: { mDevice->UpdateSubresource( mParentTexture->GetTex2D(), static_cast<UINT>(mSubresourceIndex), &dstBoxDx10, converted.data, d3dRowPitch, static_cast<UINT>(converted.slicePitch) ); if (mDevice.isError()) { String errorDescription = mDevice.getErrorDescription(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3D10 device cannot update 3d subresource\nError Description:" + errorDescription, "D3D10HardwarePixelBuffer::blitFromMemory"); } } break; } if (!isDds) { _genMipmaps(); } }
void D3D10HardwarePixelBuffer::blit(const HardwarePixelBufferSharedPtr &rsrc, const Image::Box &srcBox, const Image::Box &dstBox) { if ( (srcBox.getWidth() != dstBox.getWidth()) || (srcBox.getHeight() != dstBox.getHeight()) || (srcBox.getDepth() != dstBox.getDepth()) ) { OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3D10 device cannot copy a subresource - source and dest size are not the same and they have to be the same in DX10.", "D3D10HardwarePixelBuffer::blit"); } D3D10_BOX srcBoxDx10 = OgreImageBoxToDx10Box(srcBox); D3D10HardwarePixelBuffer * rsrcDx10 = static_cast<D3D10HardwarePixelBuffer *>(rsrc.get()); switch(mParentTexture->getTextureType()) { case TEX_TYPE_1D: { mDevice->CopySubresourceRegion( mParentTexture->GetTex1D(), static_cast<UINT>(mSubresourceIndex), static_cast<UINT>(dstBox.left), 0, 0, rsrcDx10->mParentTexture->GetTex1D(), static_cast<UINT>(rsrcDx10->mSubresourceIndex), &srcBoxDx10); if (mDevice.isError()) { String errorDescription = mDevice.getErrorDescription(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3D10 device cannot copy 1d subresource Region\nError Description:" + errorDescription, "D3D10HardwarePixelBuffer::blit"); } } break; case TEX_TYPE_CUBE_MAP: case TEX_TYPE_2D: { mDevice->CopySubresourceRegion( mParentTexture->GetTex2D(), static_cast<UINT>(mSubresourceIndex), static_cast<UINT>(dstBox.left), static_cast<UINT>(dstBox.top), mFace, rsrcDx10->mParentTexture->GetTex2D(), static_cast<UINT>(rsrcDx10->mSubresourceIndex), &srcBoxDx10); if (mDevice.isError()) { String errorDescription = mDevice.getErrorDescription(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3D10 device cannot copy 2d subresource Region\nError Description:" + errorDescription, "D3D10HardwarePixelBuffer::blit"); } } break; case TEX_TYPE_3D: { mDevice->CopySubresourceRegion( mParentTexture->GetTex2D(), static_cast<UINT>(mSubresourceIndex), static_cast<UINT>(dstBox.left), static_cast<UINT>(dstBox.top), static_cast<UINT>(dstBox.front), rsrcDx10->mParentTexture->GetTex2D(), static_cast<UINT>(rsrcDx10->mSubresourceIndex), &srcBoxDx10); if (mDevice.isError()) { String errorDescription = mDevice.getErrorDescription(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3D10 device cannot copy 3d subresource Region\nError Description:" + errorDescription, "D3D10HardwarePixelBuffer::blit"); } } break; } _genMipmaps(); }
void WriteToTexture(const String &str, TexturePtr destTexture, Image::Box destRectangle, Font* font, const ColourValue &color, char justify, bool wordwrap) { using namespace Ogre; if (destTexture->getHeight() < destRectangle.bottom) destRectangle.bottom = destTexture->getHeight(); if (destTexture->getWidth() < destRectangle.right) destRectangle.right = destTexture->getWidth(); if (!font->isLoaded()) font->load(); TexturePtr fontTexture = (TexturePtr) TextureManager::getSingleton().getByName(font->getMaterial()->getTechnique(0)->getPass(0)->getTextureUnitState(0)->getTextureName()); HardwarePixelBufferSharedPtr fontBuffer = fontTexture->getBuffer(); HardwarePixelBufferSharedPtr destBuffer = destTexture->getBuffer(); PixelBox destPb = destBuffer->lock(destRectangle,HardwareBuffer::HBL_NORMAL); // The font texture buffer was created write only...so we cannot read it back :o). One solution is to copy the buffer instead of locking it. (Maybe there is a way to create a font texture which is not write_only ?) // create a buffer size_t nBuffSize = fontBuffer->getSizeInBytes(); unsigned char* buffer = (unsigned char*)calloc(nBuffSize, sizeof(unsigned char)); // create pixel box using the copy of the buffer PixelBox fontPb(fontBuffer->getWidth(), fontBuffer->getHeight(),fontBuffer->getDepth(), fontBuffer->getFormat(), buffer); fontBuffer->blitToMemory(fontPb); unsigned char* fontData = static_cast<unsigned char*>( fontPb.data ); unsigned char* destData = static_cast<unsigned char*>( destPb.data ); const size_t fontPixelSize = PixelUtil::getNumElemBytes(fontPb.format); const size_t destPixelSize = PixelUtil::getNumElemBytes(destPb.format); const size_t fontRowPitchBytes = fontPb.rowPitch * fontPixelSize; const size_t destRowPitchBytes = destPb.rowPitch * destPixelSize; Box *GlyphTexCoords; GlyphTexCoords = new Box[str.size()]; Font::UVRect glypheTexRect; size_t charheight = 0; size_t charwidth = 0; for (unsigned int i = 0; i < str.size(); i++) { if ((str[i] != '\t') && (str[i] != '\n') && (str[i] != ' ')) { glypheTexRect = font->getGlyphTexCoords(str[i]); GlyphTexCoords[i].left = glypheTexRect.left * fontTexture->getSrcWidth(); GlyphTexCoords[i].top = glypheTexRect.top * fontTexture->getSrcHeight(); GlyphTexCoords[i].right = glypheTexRect.right * fontTexture->getSrcWidth(); GlyphTexCoords[i].bottom = glypheTexRect.bottom * fontTexture->getSrcHeight(); if (GlyphTexCoords[i].getHeight() > charheight) charheight = GlyphTexCoords[i].getHeight(); if (GlyphTexCoords[i].getWidth() > charwidth) charwidth = GlyphTexCoords[i].getWidth(); } } size_t cursorX = 0; size_t cursorY = 0; size_t lineend = destRectangle.getWidth(); bool carriagreturn = true; for (unsigned int strindex = 0; strindex < str.size(); strindex++) { switch(str[strindex]) { case ' ': cursorX += charwidth; break; case '\t': cursorX += charwidth * 3; break; case '\n': cursorY += charheight; carriagreturn = true; break; default: { //wrapping if ((cursorX + GlyphTexCoords[strindex].getWidth()> lineend) && !carriagreturn ) { cursorY += charheight; carriagreturn = true; } //justify if (carriagreturn) { size_t l = strindex; size_t textwidth = 0; size_t wordwidth = 0; while( (l < str.size() ) && (str[l] != '\n')) { wordwidth = 0; switch (str[l]) { case ' ': wordwidth = charwidth; ++l; break; case '\t': wordwidth = charwidth *3; ++l; break; case '\n': l = str.size(); } if (wordwrap) while((l < str.size()) && (str[l] != ' ') && (str[l] != '\t') && (str[l] != '\n')) { wordwidth += GlyphTexCoords[l].getWidth(); ++l; } else { wordwidth += GlyphTexCoords[l].getWidth(); l++; } if ((textwidth + wordwidth) <= destRectangle.getWidth()) textwidth += (wordwidth); else break; } if ((textwidth == 0) && (wordwidth > destRectangle.getWidth())) textwidth = destRectangle.getWidth(); switch (justify) { case 'c': cursorX = (destRectangle.getWidth() - textwidth)/2; lineend = destRectangle.getWidth() - cursorX; break; case 'r': cursorX = (destRectangle.getWidth() - textwidth); lineend = destRectangle.getWidth(); break; default: cursorX = 0; lineend = textwidth; break; } carriagreturn = false; } //abort - net enough space to draw if ((cursorY + charheight) > destRectangle.getHeight()) goto stop; //draw pixel by pixel for (size_t i = 0; i < GlyphTexCoords[strindex].getHeight(); i++ ) for (size_t j = 0; j < GlyphTexCoords[strindex].getWidth(); j++) { float alpha = color.a * (fontData[(i + GlyphTexCoords[strindex].top) * fontRowPitchBytes + (j + GlyphTexCoords[strindex].left) * fontPixelSize +1 ] / 255.0); float invalpha = 1.0 - alpha; size_t offset = (i + cursorY) * destRowPitchBytes + (j + cursorX) * destPixelSize; ColourValue pix; PixelUtil::unpackColour(&pix,destPb.format,&destData[offset]); pix = (pix * invalpha) + (color * alpha); PixelUtil::packColour(pix,destPb.format,&destData[offset]); } cursorX += GlyphTexCoords[strindex].getWidth(); }//default }//switch }//for stop: delete[] GlyphTexCoords; destBuffer->unlock(); // Free the memory allocated for the buffer free(buffer); buffer = 0; }
//----------------------------------------------------------------------------- // Very fast texture-to-texture blitter and hardware bi/trilinear scaling implementation using FBO // Destination texture must be 1D, 2D, 3D, or Cube // Source texture must be 1D, 2D or 3D // Supports compressed formats as both source and destination format, it will use the hardware DXT compressor // if available. // @author W.J. van der Laan void GLES2TextureBuffer::blitFromTexture(GLES2TextureBuffer *src, const Image::Box &srcBox, const Image::Box &dstBox) { return; // todo - add a shader attach... // std::cerr << "GLES2TextureBuffer::blitFromTexture " << // src->mTextureID << ":" << srcBox.left << "," << srcBox.top << "," << srcBox.right << "," << srcBox.bottom << " " << // mTextureID << ":" << dstBox.left << "," << dstBox.top << "," << dstBox.right << "," << dstBox.bottom << std::endl; // Store reference to FBO manager GLES2FBOManager *fboMan = static_cast<GLES2FBOManager *>(GLES2RTTManager::getSingletonPtr()); RenderSystem* rsys = Root::getSingleton().getRenderSystem(); rsys->_disableTextureUnitsFrom(0); glActiveTexture(GL_TEXTURE0); // Disable alpha, depth and scissor testing, disable blending, // and disable culling glDisable(GL_DEPTH_TEST); glDisable(GL_SCISSOR_TEST); glDisable(GL_BLEND); glDisable(GL_CULL_FACE); // Set up source texture glBindTexture(src->mTarget, src->mTextureID); GL_CHECK_ERROR; // Set filtering modes depending on the dimensions and source if(srcBox.getWidth()==dstBox.getWidth() && srcBox.getHeight()==dstBox.getHeight() && srcBox.getDepth()==dstBox.getDepth()) { // Dimensions match -- use nearest filtering (fastest and pixel correct) glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); GL_CHECK_ERROR; glTexParameteri(src->mTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); GL_CHECK_ERROR; } else { // Dimensions don't match -- use bi or trilinear filtering depending on the // source texture. if(src->mUsage & TU_AUTOMIPMAP) { // Automatic mipmaps, we can safely use trilinear filter which // brings greatly improved quality for minimisation. glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); GL_CHECK_ERROR; glTexParameteri(src->mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); GL_CHECK_ERROR; } else { // Manual mipmaps, stay safe with bilinear filtering so that no // intermipmap leakage occurs. glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); GL_CHECK_ERROR; glTexParameteri(src->mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); GL_CHECK_ERROR; } } // Clamp to edge (fastest) glTexParameteri(src->mTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); GL_CHECK_ERROR; glTexParameteri(src->mTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); GL_CHECK_ERROR; // Store old binding so it can be restored later GLint oldfb; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb); GL_CHECK_ERROR; // Set up temporary FBO glBindFramebuffer(GL_FRAMEBUFFER, fboMan->getTemporaryFBO()); GL_CHECK_ERROR; GLuint tempTex = 0; if(!fboMan->checkFormat(mFormat)) { // If target format not directly supported, create intermediate texture GLenum tempFormat = GLES2PixelUtil::getClosestGLInternalFormat(fboMan->getSupportedAlternative(mFormat)); glGenTextures(1, &tempTex); GL_CHECK_ERROR; glBindTexture(GL_TEXTURE_2D, tempTex); GL_CHECK_ERROR; #if GL_APPLE_texture_max_level glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL_APPLE, 0); GL_CHECK_ERROR; #endif // Allocate temporary texture of the size of the destination area glTexImage2D(GL_TEXTURE_2D, 0, tempFormat, GLES2PixelUtil::optionalPO2(dstBox.getWidth()), GLES2PixelUtil::optionalPO2(dstBox.getHeight()), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); GL_CHECK_ERROR; glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tempTex, 0); GL_CHECK_ERROR; // Set viewport to size of destination slice glViewport(0, 0, dstBox.getWidth(), dstBox.getHeight()); GL_CHECK_ERROR; } else { // We are going to bind directly, so set viewport to size and position of destination slice glViewport(dstBox.left, dstBox.top, dstBox.getWidth(), dstBox.getHeight()); GL_CHECK_ERROR; } // Process each destination slice for(size_t slice=dstBox.front; slice<dstBox.back; ++slice) { if(!tempTex) { // Bind directly bindToFramebuffer(GL_COLOR_ATTACHMENT0, slice); } /// Calculate source texture coordinates float u1 = (float)srcBox.left / (float)src->mWidth; float v1 = (float)srcBox.top / (float)src->mHeight; float u2 = (float)srcBox.right / (float)src->mWidth; float v2 = (float)srcBox.bottom / (float)src->mHeight; /// Calculate source slice for this destination slice float w = (float)(slice - dstBox.front) / (float)dstBox.getDepth(); /// Get slice # in source w = w * (float)srcBox.getDepth() + srcBox.front; /// Normalise to texture coordinate in 0.0 .. 1.0 w = (w+0.5f) / (float)src->mDepth; /// Finally we're ready to rumble glBindTexture(src->mTarget, src->mTextureID); GL_CHECK_ERROR; glEnable(src->mTarget); GL_CHECK_ERROR; GLfloat squareVertices[] = { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, }; GLfloat texCoords[] = { u1, v1, w, u2, v1, w, u2, v2, w, u1, v2, w }; GLuint posAttrIndex = 0; GLuint texAttrIndex = 0; if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_SEPARATE_SHADER_OBJECTS)) { GLSLESProgramPipeline* programPipeline = GLSLESProgramPipelineManager::getSingleton().getActiveProgramPipeline(); posAttrIndex = (GLuint)programPipeline->getAttributeIndex(VES_POSITION, 0); texAttrIndex = (GLuint)programPipeline->getAttributeIndex(VES_TEXTURE_COORDINATES, 0); } else { GLSLESLinkProgram* linkProgram = GLSLESLinkProgramManager::getSingleton().getActiveLinkProgram(); posAttrIndex = (GLuint)linkProgram->getAttributeIndex(VES_POSITION, 0); texAttrIndex = (GLuint)linkProgram->getAttributeIndex(VES_TEXTURE_COORDINATES, 0); } // Draw the textured quad glVertexAttribPointer(posAttrIndex, 2, GL_FLOAT, 0, 0, squareVertices); GL_CHECK_ERROR; glEnableVertexAttribArray(posAttrIndex); GL_CHECK_ERROR; glVertexAttribPointer(texAttrIndex, 3, GL_FLOAT, 0, 0, texCoords); GL_CHECK_ERROR; glEnableVertexAttribArray(texAttrIndex); GL_CHECK_ERROR; glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); GL_CHECK_ERROR; glDisable(src->mTarget); GL_CHECK_ERROR; if(tempTex) { // Copy temporary texture glBindTexture(mTarget, mTextureID); GL_CHECK_ERROR; switch(mTarget) { case GL_TEXTURE_2D: case GL_TEXTURE_CUBE_MAP: glCopyTexSubImage2D(mFaceTarget, mLevel, dstBox.left, dstBox.top, 0, 0, dstBox.getWidth(), dstBox.getHeight()); GL_CHECK_ERROR; break; } } } // Finish up if(!tempTex) { // Generate mipmaps if(mUsage & TU_AUTOMIPMAP) { glBindTexture(mTarget, mTextureID); GL_CHECK_ERROR; glGenerateMipmap(mTarget); GL_CHECK_ERROR; } } // Reset source texture to sane state glBindTexture(src->mTarget, src->mTextureID); GL_CHECK_ERROR; // Detach texture from temporary framebuffer glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); GL_CHECK_ERROR; // Restore old framebuffer glBindFramebuffer(GL_FRAMEBUFFER, oldfb); GL_CHECK_ERROR; glDeleteTextures(1, &tempTex); GL_CHECK_ERROR; }
//----------------------------------------------------------------------------- void D3D11HardwarePixelBuffer::blitFromMemory(const PixelBox &src, const Image::Box &dstBox) { bool isDds = false; switch(mFormat) { case PF_DXT1: case PF_DXT2: case PF_DXT3: case PF_DXT4: case PF_DXT5: isDds = true; break; default: break; } if (isDds && (dstBox.getWidth() % 4 != 0 || dstBox.getHeight() % 4 != 0 )) { return; } // for scoped deletion of conversion buffer MemoryDataStreamPtr buf; PixelBox converted = src; D3D11_BOX dstBoxDx11 = OgreImageBoxToDx11Box(dstBox); dstBoxDx11.front = 0; dstBoxDx11.back = converted.getDepth(); // convert to pixelbuffer's native format if necessary if (src.format != mFormat) { buf.bind(new MemoryDataStream( PixelUtil::getMemorySize(src.getWidth(), src.getHeight(), src.getDepth(), mFormat))); converted = PixelBox(src.getWidth(), src.getHeight(), src.getDepth(), mFormat, buf->getPtr()); PixelUtil::bulkPixelConversion(src, converted); } if (mUsage & HBU_DYNAMIC) { size_t sizeinbytes; if (PixelUtil::isCompressed(converted.format)) { // D3D wants the width of one row of cells in bytes if (converted.format == PF_DXT1) { // 64 bits (8 bytes) per 4x4 block sizeinbytes = std::max<size_t>(1, converted.getWidth() / 4) * std::max<size_t>(1, converted.getHeight() / 4) * 8; } else { // 128 bits (16 bytes) per 4x4 block sizeinbytes = std::max<size_t>(1, converted.getWidth() / 4) * std::max<size_t>(1, converted.getHeight() / 4) * 16; } } else { sizeinbytes = converted.getHeight() * converted.getWidth() * PixelUtil::getNumElemBytes(converted.format); } const Ogre::PixelBox &locked = lock(dstBox, HBL_DISCARD); memcpy(locked.data, converted.data, sizeinbytes); unlock(); } else { size_t rowWidth; if (PixelUtil::isCompressed(converted.format)) { // D3D wants the width of one row of cells in bytes if (converted.format == PF_DXT1) { // 64 bits (8 bytes) per 4x4 block rowWidth = (converted.rowPitch / 4) * 8; } else { // 128 bits (16 bytes) per 4x4 block rowWidth = (converted.rowPitch / 4) * 16; } } else { rowWidth = converted.rowPitch * PixelUtil::getNumElemBytes(converted.format); } switch(mParentTexture->getTextureType()) { case TEX_TYPE_1D: { D3D11RenderSystem* rsys = reinterpret_cast<D3D11RenderSystem*>(Root::getSingleton().getRenderSystem()); if (rsys->_getFeatureLevel() >= D3D_FEATURE_LEVEL_10_0) { mDevice.GetImmediateContext()->UpdateSubresource( mParentTexture->GetTex1D(), 0, &dstBoxDx11, converted.data, rowWidth, 0 ); if (mDevice.isError()) { String errorDescription = mDevice.getErrorDescription(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3D11 device cannot update 1d subresource\nError Description:" + errorDescription, "D3D11HardwarePixelBuffer::blitFromMemory"); } break; // For Feature levels that do not support 1D textures, revert to creating a 2D texture. } } case TEX_TYPE_CUBE_MAP: case TEX_TYPE_2D: { mDevice.GetImmediateContext()->UpdateSubresource( mParentTexture->GetTex2D(), D3D11CalcSubresource(static_cast<UINT>(mSubresourceIndex), mFace, mParentTexture->getNumMipmaps()+1), &dstBoxDx11, converted.data, rowWidth, 0 ); if (mDevice.isError()) { String errorDescription = mDevice.getErrorDescription(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3D11 device cannot update 2d subresource\nError Description:" + errorDescription, "D3D11HardwarePixelBuffer::blitFromMemory"); } } break; case TEX_TYPE_2D_ARRAY: { mDevice.GetImmediateContext()->UpdateSubresource( mParentTexture->GetTex2D(), D3D11CalcSubresource(static_cast<UINT>(mSubresourceIndex), src.front, mParentTexture->getNumMipmaps()+1), &dstBoxDx11, converted.data, rowWidth, 0 ); if (mDevice.isError()) { String errorDescription = mDevice.getErrorDescription(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3D11 device cannot update 2d array subresource\nError Description:" + errorDescription, "D3D11HardwarePixelBuffer::blitFromMemory"); } } break; case TEX_TYPE_3D: { // copied from dx9 size_t sliceWidth; if (PixelUtil::isCompressed(converted.format)) { // D3D wants the width of one slice of cells in bytes if (converted.format == PF_DXT1) { // 64 bits (8 bytes) per 4x4 block sliceWidth = (converted.slicePitch / 16) * 8; } else { // 128 bits (16 bytes) per 4x4 block sliceWidth = (converted.slicePitch / 16) * 16; } } else { sliceWidth = converted.slicePitch * PixelUtil::getNumElemBytes(converted.format); } mDevice.GetImmediateContext()->UpdateSubresource( mParentTexture->GetTex3D(), static_cast<UINT>(mSubresourceIndex), &dstBoxDx11, converted.data, rowWidth, sliceWidth ); if (mDevice.isError()) { String errorDescription = mDevice.getErrorDescription(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "D3D11 device cannot update 3d subresource\nError Description:" + errorDescription, "D3D11HardwarePixelBuffer::blitFromMemory"); } } break; } if (!isDds) { _genMipmaps(); } } }
//----------------------------------------------------------------------------- PixelBox D3D11HardwarePixelBuffer::lockImpl(const Image::Box lockBox, LockOptions options) { // Check for misuse if(mUsage & TU_RENDERTARGET) OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "DirectX does not allow locking of or directly writing to RenderTargets. Use blitFromMemory if you need the contents.", "D3D11HardwarePixelBuffer::lockImpl"); mLockBox = lockBox; // Set extents and format // Note that we do not carry over the left/top/front here, since the returned // PixelBox will be re-based from the locking point onwards PixelBox rval(lockBox.getWidth(), lockBox.getHeight(), lockBox.getDepth(), mFormat); // Set locking flags according to options D3D11_MAP flags = D3D11_MAP_WRITE_DISCARD ; switch(options) { case HBL_NO_OVERWRITE: flags = D3D11_MAP_WRITE_NO_OVERWRITE; break; case HBL_NORMAL: flags = D3D11_MAP_READ_WRITE; break; case HBL_DISCARD: flags = D3D11_MAP_WRITE_DISCARD; break; case HBL_READ_ONLY: flags = D3D11_MAP_READ; break; case HBL_WRITE_ONLY: flags = D3D11_MAP_WRITE; break; default: break; }; size_t offset = 0; if(mUsage == HBU_STATIC || mUsage & HBU_DYNAMIC) { if(mUsage == HBU_STATIC || options == HBL_READ_ONLY || options == HBL_NORMAL || options == HBL_WRITE_ONLY) rval.data = _mapstagingbuffer(flags); else _map(mParentTexture->getTextureResource(), flags, rval); // calculate the offset in bytes offset = D3D11Mappings::_getSizeInBytes(rval.format, rval.left, rval.front); // add the offset, so the right memory will be changed //rval.data = static_cast<int*>(rval.data) + offset; } else { size_t sizeOfImage = rval.getConsecutiveSize(); mDataForStaticUsageLock = new int8[sizeOfImage]; rval.data = mDataForStaticUsageLock; } // save without offset mCurrentLock = rval; mCurrentLockOptions = options; // add the offset, so the right memory will be changed rval.data = static_cast<int*>(rval.data) + offset; return rval; }
//----------------------------------------------------------------------------- // blitFromMemory doing hardware trilinear scaling void GLES2TextureBuffer::blitFromMemory(const PixelBox &src_orig, const Image::Box &dstBox) { // Fall back to normal GLHardwarePixelBuffer::blitFromMemory in case // - FBO is not supported // - Either source or target is luminance due doesn't looks like supported by hardware // - the source dimensions match the destination ones, in which case no scaling is needed // TODO: Check that extension is NOT available if(PixelUtil::isLuminance(src_orig.format) || PixelUtil::isLuminance(mFormat) || (src_orig.getWidth() == dstBox.getWidth() && src_orig.getHeight() == dstBox.getHeight() && src_orig.getDepth() == dstBox.getDepth())) { GLES2HardwarePixelBuffer::blitFromMemory(src_orig, dstBox); return; } if(!mBuffer.contains(dstBox)) OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Destination box out of range", "GLES2TextureBuffer::blitFromMemory"); // For scoped deletion of conversion buffer MemoryDataStreamPtr buf; PixelBox src; // First, convert the srcbox to a OpenGL compatible pixel format if(GLES2PixelUtil::getGLOriginFormat(src_orig.format) == 0) { // Convert to buffer internal format buf.bind(OGRE_NEW MemoryDataStream(PixelUtil::getMemorySize(src_orig.getWidth(), src_orig.getHeight(), src_orig.getDepth(), mFormat))); src = PixelBox(src_orig.getWidth(), src_orig.getHeight(), src_orig.getDepth(), mFormat, buf->getPtr()); PixelUtil::bulkPixelConversion(src_orig, src); } else { // No conversion needed src = src_orig; } // Create temporary texture to store source data GLuint id; GLenum target = #if OGRE_NO_GLES3_SUPPORT == 0 (src.getDepth() != 1) ? GL_TEXTURE_3D : #endif GL_TEXTURE_2D; GLsizei width = GLES2PixelUtil::optionalPO2(src.getWidth()); GLsizei height = GLES2PixelUtil::optionalPO2(src.getHeight()); GLenum format = GLES2PixelUtil::getClosestGLInternalFormat(src.format); GLenum datatype = GLES2PixelUtil::getGLOriginDataType(src.format); // Generate texture name OGRE_CHECK_GL_ERROR(glGenTextures(1, &id)); // Set texture type OGRE_CHECK_GL_ERROR(glBindTexture(target, id)); #if GL_APPLE_texture_max_level && OGRE_PLATFORM != OGRE_PLATFORM_NACL OGRE_CHECK_GL_ERROR(glTexParameteri(target, GL_TEXTURE_MAX_LEVEL_APPLE, 1000 )); #elif OGRE_NO_GLES3_SUPPORT == 0 OGRE_CHECK_GL_ERROR(glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, 1000 )); #endif // Allocate texture memory #if OGRE_NO_GLES3_SUPPORT == 0 if(target == GL_TEXTURE_3D || target == GL_TEXTURE_2D_ARRAY) glTexImage3D(target, 0, src.format, src.getWidth(), src.getHeight(), src.getDepth(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); else #endif OGRE_CHECK_GL_ERROR(glTexImage2D(target, 0, format, width, height, 0, format, datatype, 0)); // GL texture buffer GLES2TextureBuffer tex(StringUtil::BLANK, target, id, width, height, format, src.format, 0, 0, (Usage)(TU_AUTOMIPMAP|HBU_STATIC_WRITE_ONLY), false, false, 0); // Upload data to 0,0,0 in temporary texture Image::Box tempTarget(0, 0, 0, src.getWidth(), src.getHeight(), src.getDepth()); tex.upload(src, tempTarget); // Blit blitFromTexture(&tex, tempTarget, dstBox); // Delete temp texture OGRE_CHECK_GL_ERROR(glDeleteTextures(1, &id)); }
void GLES2TextureBuffer::upload(const PixelBox &data, const Image::Box &dest) { OGRE_CHECK_GL_ERROR(glBindTexture(mTarget, mTextureID)); if (PixelUtil::isCompressed(data.format)) { if(data.format != mFormat || !data.isConsecutive()) OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Compressed images must be consecutive, in the source format", "GLES2TextureBuffer::upload"); GLenum format = GLES2PixelUtil::getClosestGLInternalFormat(mFormat); // Data must be consecutive and at beginning of buffer as PixelStorei not allowed // for compressed formats if (dest.left == 0 && dest.top == 0) { OGRE_CHECK_GL_ERROR(glCompressedTexImage2D(mFaceTarget, mLevel, format, dest.getWidth(), dest.getHeight(), 0, data.getConsecutiveSize(), data.data)); } else { OGRE_CHECK_GL_ERROR(glCompressedTexSubImage2D(mFaceTarget, mLevel, dest.left, dest.top, dest.getWidth(), dest.getHeight(), format, data.getConsecutiveSize(), data.data)); } } else if (mSoftwareMipmap) { if (data.getWidth() != data.rowPitch) { #if OGRE_NO_GLES3_SUPPORT == 0 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, data.rowPitch)); #else OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLES2TextureBuffer::upload"); #endif } if (data.getHeight() * data.getWidth() != data.slicePitch) { #if OGRE_NO_GLES3_SUPPORT == 0 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, (data.slicePitch/data.getWidth()))); #else OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLES2TextureBuffer::upload"); #endif } #if OGRE_NO_GLES3_SUPPORT == 0 if(data.left > 0 || data.top > 0 || data.front > 0) OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_SKIP_PIXELS, data.left + data.rowPitch * data.top + data.slicePitch * data.front)); #endif OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); buildMipmaps(data); } else { if (data.getWidth() != data.rowPitch) { #if OGRE_NO_GLES3_SUPPORT == 0 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, data.rowPitch)); #else OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLES2TextureBuffer::upload"); #endif } if (data.getHeight() * data.getWidth() != data.slicePitch) { #if OGRE_NO_GLES3_SUPPORT == 0 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, (data.slicePitch/data.getWidth()))); #else OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLES2TextureBuffer::upload"); #endif } #if OGRE_NO_GLES3_SUPPORT == 0 if(data.left > 0 || data.top > 0 || data.front > 0) OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_SKIP_PIXELS, data.left + data.rowPitch * data.top + data.slicePitch * data.front)); #endif if ((data.getWidth() * PixelUtil::getNumElemBytes(data.format)) & 3) { // Standard alignment of 4 is not right OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); } // LogManager::getSingleton().logMessage("GLES2TextureBuffer::upload - ID: " + StringConverter::toString(mTextureID) + // " Format: " + PixelUtil::getFormatName(data.format) + // " Origin format: " + StringConverter::toString(GLES2PixelUtil::getGLOriginFormat(data.format), 0, std::ios::hex) + // " Data type: " + StringConverter::toString(GLES2PixelUtil::getGLOriginDataType(data.format), 0, ' ', std::ios::hex)); OGRE_CHECK_GL_ERROR(glTexSubImage2D(mFaceTarget, mLevel, dest.left, dest.top, dest.getWidth(), dest.getHeight(), GLES2PixelUtil::getGLOriginFormat(data.format), GLES2PixelUtil::getGLOriginDataType(data.format), data.data)); } #if OGRE_NO_GLES3_SUPPORT == 0 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)); OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0)); OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0)); #endif OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, 4)); }
//----------------------------------------------------------------------------- // Very fast texture-to-texture blitter and hardware bi/trilinear scaling implementation using FBO // Destination texture must be 2D // Source texture must be 2D // Supports compressed formats as both source and destination format, it will use the hardware DXT compressor // if available. // @author W.J. van der Laan void GLESTextureBuffer::blitFromTexture(GLESTextureBuffer *src, const Image::Box &srcBox, const Image::Box &dstBox) { if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_FBO) == false) { // the following code depends on FBO support, it crashes if FBO is not supported. // TODO - write PBUFFER version of this function or a version that doesn't require FBO return; // for now - do nothing. } // std::cerr << "GLESTextureBuffer::blitFromTexture " << // src->mTextureID << ":" << srcBox.left << "," << srcBox.top << "," << srcBox.right << "," << srcBox.bottom << " " << // mTextureID << ":" << dstBox.left << "," << dstBox.top << "," << dstBox.right << "," << dstBox.bottom << std::endl; // Store reference to FBO manager GLESFBOManager *fboMan = static_cast<GLESFBOManager *>(GLESRTTManager::getSingletonPtr()); // Save and clear GL state for rendering glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); RenderSystem* rsys = Root::getSingleton().getRenderSystem(); rsys->_disableTextureUnitsFrom(0); // Disable alpha, depth and scissor testing, disable blending, // disable culling, disble lighting, disable fog and reset foreground // colour. glDisable(GL_ALPHA_TEST); glDisable(GL_DEPTH_TEST); glDisable(GL_SCISSOR_TEST); glDisable(GL_BLEND); glDisable(GL_CULL_FACE); glDisable(GL_LIGHTING); glDisable(GL_FOG); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); GL_CHECK_ERROR; // Save and reset matrices glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_TEXTURE); glPushMatrix(); glLoadIdentity(); GL_CHECK_ERROR; // Set up source texture glBindTexture(src->mTarget, src->mTextureID); GL_CHECK_ERROR; // Set filtering modes depending on the dimensions and source if(srcBox.getWidth()==dstBox.getWidth() && srcBox.getHeight()==dstBox.getHeight() && srcBox.getDepth()==dstBox.getDepth()) { // Dimensions match -- use nearest filtering (fastest and pixel correct) glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); GL_CHECK_ERROR; glTexParameteri(src->mTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); GL_CHECK_ERROR; } else { // Dimensions don't match -- use bi or trilinear filtering depending on the // source texture. if(src->mUsage & TU_AUTOMIPMAP) { // Automatic mipmaps, we can safely use trilinear filter which // brings greatly imporoved quality for minimisation. glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); GL_CHECK_ERROR; glTexParameteri(src->mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); GL_CHECK_ERROR; } else { // Manual mipmaps, stay safe with bilinear filtering so that no // intermipmap leakage occurs. glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); GL_CHECK_ERROR; glTexParameteri(src->mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); GL_CHECK_ERROR; } } // Clamp to edge (fastest) glTexParameteri(src->mTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); GL_CHECK_ERROR; glTexParameteri(src->mTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); GL_CHECK_ERROR; // Store old binding so it can be restored later GLint oldfb; glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &oldfb); GL_CHECK_ERROR; // Set up temporary FBO glBindFramebufferOES(GL_FRAMEBUFFER_OES, fboMan->getTemporaryFBO()); GL_CHECK_ERROR; GLuint tempTex = 0; if(!fboMan->checkFormat(mFormat)) { // If target format not directly supported, create intermediate texture GLenum tempFormat = GLESPixelUtil::getClosestGLInternalFormat(fboMan->getSupportedAlternative(mFormat)); glGenTextures(1, &tempTex); GL_CHECK_ERROR; glBindTexture(GL_TEXTURE_2D, tempTex); GL_CHECK_ERROR; // Allocate temporary texture of the size of the destination area glTexImage2D(GL_TEXTURE_2D, 0, tempFormat, GLESPixelUtil::optionalPO2(dstBox.getWidth()), GLESPixelUtil::optionalPO2(dstBox.getHeight()), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); GL_CHECK_ERROR; glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tempTex, 0); GL_CHECK_ERROR; // Set viewport to size of destination slice glViewport(0, 0, dstBox.getWidth(), dstBox.getHeight()); GL_CHECK_ERROR; } else { // We are going to bind directly, so set viewport to size and position of destination slice glViewport(dstBox.left, dstBox.top, dstBox.getWidth(), dstBox.getHeight()); GL_CHECK_ERROR; } // Process each destination slice for(size_t slice=dstBox.front; slice<dstBox.back; ++slice) { if(!tempTex) { /// Bind directly bindToFramebuffer(GL_COLOR_ATTACHMENT0_OES, slice); } if(tempTex) { // Copy temporary texture glBindTexture(mTarget, mTextureID); GL_CHECK_ERROR; switch(mTarget) { case GL_TEXTURE_2D: #if OGRE_PLATFORM == OGRE_PLATFORM_ANDROID case GL_TEXTURE_CUBE_MAP_OES: #endif glCopyTexSubImage2D(mFaceTarget, mLevel, dstBox.left, dstBox.top, 0, 0, dstBox.getWidth(), dstBox.getHeight()); GL_CHECK_ERROR; break; } } } // Finish up if(!tempTex) { // Generate mipmaps if(mUsage & TU_AUTOMIPMAP) { glBindTexture(mTarget, mTextureID); GL_CHECK_ERROR; glGenerateMipmapOES(mTarget); GL_CHECK_ERROR; } } // Reset source texture to sane state glBindTexture(src->mTarget, src->mTextureID); GL_CHECK_ERROR; // Detach texture from temporary framebuffer glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, 0); GL_CHECK_ERROR; // Restore old framebuffer glBindFramebufferOES(GL_FRAMEBUFFER_OES, oldfb); GL_CHECK_ERROR; // Restore matrix stacks and render state glMatrixMode(GL_TEXTURE); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); GL_CHECK_ERROR; glDeleteTextures(1, &tempTex); GL_CHECK_ERROR; }
//----------------------------------------------------------------------------- // blitFromMemory doing hardware trilinear scaling void GLESTextureBuffer::blitFromMemory(const PixelBox &src_orig, const Image::Box &dstBox) { // Fall back to normal GLHardwarePixelBuffer::blitFromMemory in case // - FBO is not supported // - Either source or target is luminance due doesn't looks like supported by hardware // - the source dimensions match the destination ones, in which case no scaling is needed if(!GL_OES_framebuffer_object || PixelUtil::isLuminance(src_orig.format) || PixelUtil::isLuminance(mFormat) || (src_orig.getWidth() == dstBox.getWidth() && src_orig.getHeight() == dstBox.getHeight() && src_orig.getDepth() == dstBox.getDepth())) { GLESHardwarePixelBuffer::blitFromMemory(src_orig, dstBox); return; } if(!mBuffer.contains(dstBox)) OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Destination box out of range", "GLESTextureBuffer::blitFromMemory"); // For scoped deletion of conversion buffer MemoryDataStreamPtr buf; PixelBox src; // First, convert the srcbox to a OpenGL compatible pixel format if(GLESPixelUtil::getGLOriginFormat(src_orig.format) == 0) { // Convert to buffer internal format buf.bind(OGRE_NEW MemoryDataStream(PixelUtil::getMemorySize(src_orig.getWidth(), src_orig.getHeight(), src_orig.getDepth(), mFormat))); src = PixelBox(src_orig.getWidth(), src_orig.getHeight(), src_orig.getDepth(), mFormat, buf->getPtr()); PixelUtil::bulkPixelConversion(src_orig, src); } else { // No conversion needed src = src_orig; } // Create temporary texture to store source data GLuint id; GLenum target = GL_TEXTURE_2D; GLsizei width = GLESPixelUtil::optionalPO2(src.getWidth()); GLsizei height = GLESPixelUtil::optionalPO2(src.getHeight()); GLenum format = GLESPixelUtil::getClosestGLInternalFormat(src.format); GLenum datatype = GLESPixelUtil::getGLOriginDataType(src.format); // Generate texture name glGenTextures(1, &id); GL_CHECK_ERROR; // Set texture type glBindTexture(target, id); GL_CHECK_ERROR; // Set automatic mipmap generation; nice for minimisation glTexParameteri(target, GL_GENERATE_MIPMAP, GL_TRUE ); GL_CHECK_ERROR; // Allocate texture memory glTexImage2D(target, 0, format, width, height, 0, format, datatype, 0); GL_CHECK_ERROR; // GL texture buffer GLESTextureBuffer tex(BLANKSTRING, target, id, width, height, format, src.format, 0, 0, (Usage)(TU_AUTOMIPMAP|HBU_STATIC_WRITE_ONLY), false, false, 0); // Upload data to 0,0,0 in temporary texture Image::Box tempTarget(0, 0, 0, src.getWidth(), src.getHeight(), src.getDepth()); tex.upload(src, tempTarget); // Blit blitFromTexture(&tex, tempTarget, dstBox); // Delete temp texture glDeleteTextures(1, &id); GL_CHECK_ERROR; }
void GLES2TextureBuffer::upload(const PixelBox &data, const Image::Box &dest) { glBindTexture(mTarget, mTextureID); GL_CHECK_ERROR; if (PixelUtil::isCompressed(data.format)) { if(data.format != mFormat || !data.isConsecutive()) OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Compressed images must be consecutive, in the source format", "GLES2TextureBuffer::upload"); GLenum format = GLES2PixelUtil::getClosestGLInternalFormat(mFormat); // Data must be consecutive and at beginning of buffer as PixelStorei not allowed // for compressed formats if (dest.left == 0 && dest.top == 0) { glCompressedTexImage2D(mFaceTarget, mLevel, format, dest.getWidth(), dest.getHeight(), 0, data.getConsecutiveSize(), data.data); GL_CHECK_ERROR; } else { glCompressedTexSubImage2D(mFaceTarget, mLevel, dest.left, dest.top, dest.getWidth(), dest.getHeight(), format, data.getConsecutiveSize(), data.data); GL_CHECK_ERROR; } } else if (mSoftwareMipmap) { if (data.getWidth() != data.rowPitch) { // TODO OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLES2TextureBuffer::upload"); } if (data.getHeight() * data.getWidth() != data.slicePitch) { // TODO OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLES2TextureBuffer::upload"); } glPixelStorei(GL_UNPACK_ALIGNMENT, 1); GL_CHECK_ERROR; buildMipmaps(data); } else { if(data.getWidth() != data.rowPitch) { // TODO OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLES2TextureBuffer::upload"); } if(data.getHeight()*data.getWidth() != data.slicePitch) { // TODO OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLES2TextureBuffer::upload"); } if ((data.getWidth() * PixelUtil::getNumElemBytes(data.format)) & 3) { // Standard alignment of 4 is not right glPixelStorei(GL_UNPACK_ALIGNMENT, 1); GL_CHECK_ERROR; } glTexSubImage2D(mFaceTarget, mLevel, dest.left, dest.top, dest.getWidth(), dest.getHeight(), GLES2PixelUtil::getGLOriginFormat(data.format), GLES2PixelUtil::getGLOriginDataType(data.format), data.data); } if ((mUsage & TU_AUTOMIPMAP) && !mSoftwareMipmap && (mLevel == 0)) { glGenerateMipmap(mFaceTarget); GL_CHECK_ERROR; } glPixelStorei(GL_UNPACK_ALIGNMENT, 4); GL_CHECK_ERROR; }