bool IsDepthStencilFormat(GLenum internalFormat) { if(IsCompressedFormat(internalFormat)) return false; GLenum fmt = GetBaseFormat(internalFormat); return (fmt == eGL_DEPTH_COMPONENT || fmt == eGL_STENCIL || fmt == eGL_DEPTH_STENCIL); }
//-------------------------------------------------------------------------------------- // PixelBuffer //-------------------------------------------------------------------------------------- D3D12_RESOURCE_DESC PixelBuffer::DescribeTex2D( uint32_t Width, uint32_t Height, uint32_t DepthOrArraySize, uint32_t NumMips, DXGI_FORMAT Format, UINT Flags ) { m_Width = Width; m_Height = Height; m_ArraySize = DepthOrArraySize; m_Format = Format; D3D12_RESOURCE_DESC Desc = {}; Desc.Alignment = 0; Desc.DepthOrArraySize = (UINT16)DepthOrArraySize; Desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; Desc.Flags = (D3D12_RESOURCE_FLAGS)Flags; Desc.Format = GetBaseFormat( Format ); Desc.Height = (UINT)Height; Desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; Desc.MipLevels = NumMips; Desc.SampleDesc.Count = 1; Desc.SampleDesc.Quality = 0; Desc.Width = (UINT64)Width; return Desc; }
void Texture2D::SetPixels(byte* pixels) { GL(glBindTexture(GL_TEXTURE_2D, m_Texture)); GL(glTexImage2D(GL_TEXTURE_2D, 0, static_cast<GLenum>(m_Format), m_Width, m_Height, 0, static_cast<GLenum>(GetBaseFormat(m_Format)), GL_UNSIGNED_BYTE, pixels)); }
Texture2D::Texture2D(byte* pixels, u32 width, u32 height, TextureFormat format) : Texture(width, height, TextureType::TEXTURE_2D, format) { GL(glGenTextures(1, &m_Texture)); GL(glBindTexture(GL_TEXTURE_2D, m_Texture)); GL(glTexImage2D(GL_TEXTURE_2D, 0, static_cast<GLenum>(format), width, height, 0, static_cast<GLenum>(GetBaseFormat(format)), GL_UNSIGNED_BYTE, pixels)); SetSamplerState(); }
size_t GetCompressedByteSize(GLsizei w, GLsizei h, GLsizei d, GLenum internalformat, int mip) { if(!IsCompressedFormat(internalformat)) { RDCERR("Not compressed format"); return GetByteSize(w, h, d, GetBaseFormat(internalformat), GetDataType(internalformat)); } switch(internalformat) { // BC1 case eGL_COMPRESSED_RGB_S3TC_DXT1_EXT: case eGL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case eGL_COMPRESSED_SRGB_S3TC_DXT1_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: return (AlignUp4(w) * AlignUp4(h) * d) / 2; // BC2 case eGL_COMPRESSED_RGBA_S3TC_DXT3_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: return (AlignUp4(w) * AlignUp4(h) * d); // BC3 case eGL_COMPRESSED_RGBA_S3TC_DXT5_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: return (AlignUp4(w) * AlignUp4(h) * d); // BC4 case eGL_COMPRESSED_RED_RGTC1: case eGL_COMPRESSED_SIGNED_RED_RGTC1: return (AlignUp4(w) * AlignUp4(h) * d) / 2; // BC5 case eGL_COMPRESSED_RG_RGTC2: case eGL_COMPRESSED_SIGNED_RG_RGTC2: return (AlignUp4(w) * AlignUp4(h) * d); // BC6 case eGL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB: case eGL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB: return (AlignUp4(w) * AlignUp4(h) * d); // BC7 case eGL_COMPRESSED_RGBA_BPTC_UNORM_ARB: case eGL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB: return (AlignUp4(w) * AlignUp4(h) * d); // ETC2 case eGL_COMPRESSED_RGB8_ETC2: case eGL_COMPRESSED_SRGB8_ETC2: case eGL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: case eGL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: return (AlignUp4(w) * AlignUp4(h) * d) / 2; // EAC case eGL_COMPRESSED_RGBA8_ETC2_EAC: case eGL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: return (AlignUp4(w) * AlignUp4(h) * d); case eGL_COMPRESSED_R11_EAC: case eGL_COMPRESSED_SIGNED_R11_EAC: return (AlignUp4(w) * AlignUp4(h) * d) / 2; case eGL_COMPRESSED_RG11_EAC: case eGL_COMPRESSED_SIGNED_RG11_EAC: return (AlignUp4(w) * AlignUp4(h) * d); default: break; } RDCERR("Unrecognised compressed format"); return GetByteSize(w, h, d, GetBaseFormat(internalformat), GetDataType(internalformat)); }
size_t GetCompressedByteSize(GLsizei w, GLsizei h, GLsizei d, GLenum internalformat, int mip) { if(!IsCompressedFormat(internalformat)) { RDCERR("Not compressed format %s", ToStr::Get(internalformat).c_str()); return GetByteSize(w, h, d, GetBaseFormat(internalformat), GetDataType(internalformat)); } uint32_t astc[2] = {0, 0}; switch(internalformat) { // BC1 case eGL_COMPRESSED_RGB_S3TC_DXT1_EXT: case eGL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case eGL_COMPRESSED_SRGB_S3TC_DXT1_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: return (AlignUp4(w) * AlignUp4(h) * d) / 2; // BC2 case eGL_COMPRESSED_RGBA_S3TC_DXT3_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: return (AlignUp4(w) * AlignUp4(h) * d); // BC3 case eGL_COMPRESSED_RGBA_S3TC_DXT5_EXT: case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: return (AlignUp4(w) * AlignUp4(h) * d); // BC4 case eGL_COMPRESSED_RED_RGTC1: case eGL_COMPRESSED_SIGNED_RED_RGTC1: return (AlignUp4(w) * AlignUp4(h) * d) / 2; // BC5 case eGL_COMPRESSED_RG_RGTC2: case eGL_COMPRESSED_SIGNED_RG_RGTC2: return (AlignUp4(w) * AlignUp4(h) * d); // BC6 case eGL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB: case eGL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB: return (AlignUp4(w) * AlignUp4(h) * d); // BC7 case eGL_COMPRESSED_RGBA_BPTC_UNORM_ARB: case eGL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB: return (AlignUp4(w) * AlignUp4(h) * d); // ETC2 case eGL_COMPRESSED_RGB8_ETC2: case eGL_COMPRESSED_SRGB8_ETC2: case eGL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: case eGL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: return (AlignUp4(w) * AlignUp4(h) * d) / 2; // EAC case eGL_COMPRESSED_RGBA8_ETC2_EAC: case eGL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: return (AlignUp4(w) * AlignUp4(h) * d); case eGL_COMPRESSED_R11_EAC: case eGL_COMPRESSED_SIGNED_R11_EAC: return (AlignUp4(w) * AlignUp4(h) * d) / 2; case eGL_COMPRESSED_RG11_EAC: case eGL_COMPRESSED_SIGNED_RG11_EAC: return (AlignUp4(w) * AlignUp4(h) * d); case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: astc[0] = 4; astc[1] = 4; break; case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: astc[0] = 5; astc[1] = 4; break; case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: astc[0] = 5; astc[1] = 5; break; case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: astc[0] = 6; astc[1] = 5; break; case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: astc[0] = 6; astc[1] = 6; break; case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: astc[0] = 8; astc[1] = 5; break; case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: astc[0] = 8; astc[1] = 6; break; case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: astc[0] = 8; astc[1] = 8; break; case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: astc[0] = 10; astc[1] = 5; break; case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: astc[0] = 10; astc[1] = 6; break; case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: astc[0] = 10; astc[1] = 8; break; case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: astc[0] = 10; astc[1] = 10; break; case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: astc[0] = 12; astc[1] = 10; break; case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: astc[0] = 12; astc[1] = 12; break; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: astc[0] = 4; astc[1] = 4; break; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: astc[0] = 5; astc[1] = 4; break; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: astc[0] = 5; astc[1] = 5; break; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: astc[0] = 6; astc[1] = 5; break; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: astc[0] = 6; astc[1] = 6; break; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: astc[0] = 8; astc[1] = 5; break; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: astc[0] = 8; astc[1] = 6; break; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: astc[0] = 8; astc[1] = 8; break; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: astc[0] = 10; astc[1] = 5; break; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: astc[0] = 10; astc[1] = 6; break; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: astc[0] = 10; astc[1] = 8; break; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: astc[0] = 10; astc[1] = 10; break; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: astc[0] = 12; astc[1] = 10; break; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: astc[0] = 12; astc[1] = 12; break; default: break; } if(astc[0] > 0 && astc[1] > 0) { uint32_t blocks[2] = {(w / astc[0]), (h / astc[1])}; // how many blocks are needed - including any extra partial blocks blocks[0] += (w % astc[0]) ? 1 : 0; blocks[1] += (h % astc[1]) ? 1 : 0; // ASTC blocks are all 128 bits each return blocks[0] * blocks[1] * 16 * d; } RDCERR("Unrecognised compressed format %s", ToStr::Get(internalformat).c_str()); return GetByteSize(w, h, d, GetBaseFormat(internalformat), GetDataType(internalformat)); }
void csGLRender2TextureFramebuf::GrabFramebuffer (const RTAttachment<>& target, InternalFormatClass fmtClass) { csGLBasicTextureHandle* tex_mm = static_cast<csGLBasicTextureHandle*> ((iTextureHandle*)target.texture); tex_mm->Precache (); // Texture is in tha cache, update texture directly. G3D->ActivateTexture (tex_mm); GLenum internalFormat = 0; /* Texture has a keycolor - so we need to deal specially with it * to make sure the keycolor gets transparent. */ if (tex_mm->GetKeyColor ()) { // @@@ Processing sucks, but how else to handle keycolor? const size_t numPix = txt_w * txt_h; pixelScratch.SetSize (numPix * 4); glReadPixels (0, 0, txt_w, txt_h, GL_RGBA, GL_UNSIGNED_BYTE, pixelScratch.GetArray()); csRGBpixel key; tex_mm->GetKeyColor (key.red, key.green, key.blue); csBakeKeyColor::RGBA2D (pixelScratch.GetArray(), pixelScratch.GetArray(), txt_w, txt_h, key); tex_mm->Blit (0, 0, txt_w, txt_h, pixelScratch.GetArray()); } else { GLenum textarget = tex_mm->GetGLTextureTarget(); if ((textarget != GL_TEXTURE_2D) && (textarget != GL_TEXTURE_3D) && (textarget != GL_TEXTURE_RECTANGLE_ARB) && (textarget != GL_TEXTURE_CUBE_MAP)) return; bool handle_subtexture = (textarget == GL_TEXTURE_CUBE_MAP); bool handle_3d = (textarget == GL_TEXTURE_3D); /* Reportedly, some drivers crash if using CopyTexImage on a texture * size larger than the framebuffer. Use CopyTexSubImage then. */ bool needSubImage = (txt_w > viewportHelper.GetVPWidth()) || (txt_h > viewportHelper.GetVPHeight()); // Texture was not used as a render target before. // Make some necessary adjustments. if (needSubImage) { if (!tex_mm->IsWasRenderTarget()) { internalFormat = GetInternalFormat (fmtClass, tex_mm); GLenum baseFormat = GetBaseFormat (fmtClass, tex_mm); tex_mm->SetupAutoMipping(); tex_mm->SetWasRenderTarget (true); tex_mm->texFormat = iTextureHandle::RGBA8888; // Gah. Turn target texture to required storage. if (handle_subtexture) { for (int i = 0; i < 6; i++) glTexImage2D ( GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + i, 0, internalFormat, txt_w, txt_h,0, baseFormat, GL_UNSIGNED_BYTE, 0); } else if (handle_3d) { G3D->ext->glTexImage3D (textarget, 0, internalFormat, txt_w, txt_h, txt_d, 0, baseFormat, GL_UNSIGNED_BYTE, 0); } else glTexImage2D (textarget, 0, internalFormat, txt_w, txt_h, 0, baseFormat, GL_UNSIGNED_BYTE, 0); } int orgX = viewportHelper.GetVPOfsX(); int orgY = viewportHelper.GetOriginalFramebufferHeight() - (viewportHelper.GetVPOfsY() + csMin (txt_h, viewportHelper.GetVPHeight())); if (handle_subtexture) glCopyTexSubImage2D ( GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + target.subtexture, 0, 0, 0, orgX, orgY, csMin (txt_w, viewportHelper.GetVPWidth()), csMin (txt_h, viewportHelper.GetVPHeight())); else if (handle_3d) G3D->ext->glCopyTexSubImage3D (textarget, 0, 0, 0, orgX, orgY, target.subtexture, csMin (txt_w, viewportHelper.GetVPWidth()), csMin (txt_h, viewportHelper.GetVPHeight())); else glCopyTexSubImage2D (textarget, 0, 0, 0, orgX, orgY, csMin (txt_w, viewportHelper.GetVPWidth()), csMin (txt_h, viewportHelper.GetVPHeight())); } else { int orgX = viewportHelper.GetVPOfsX(); int orgY = viewportHelper.GetOriginalFramebufferHeight() - (viewportHelper.GetVPOfsY() + txt_h); if (!tex_mm->IsWasRenderTarget()) { internalFormat = GetInternalFormat (fmtClass, tex_mm); tex_mm->SetupAutoMipping(); tex_mm->SetWasRenderTarget (true); tex_mm->texFormat = iTextureHandle::RGBA8888; if (handle_subtexture) glCopyTexImage2D (GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + target.subtexture, 0, internalFormat, orgX, orgY, txt_w, txt_h, 0); else if (handle_3d) { // Gah. Turn target texture to required storage. GLenum baseFormat = GetBaseFormat (fmtClass, tex_mm); G3D->ext->glTexImage3D (textarget, 0, internalFormat, txt_w, txt_h, txt_d, 0, baseFormat, GL_UNSIGNED_BYTE, 0); G3D->ext->glCopyTexSubImage3D (textarget, 0, 0, 0, orgX, orgY, target.subtexture, txt_w, txt_h); } else glCopyTexImage2D (textarget, 0, internalFormat, orgX, orgY, txt_w, txt_h, 0); } else { glGetTexLevelParameteriv ((textarget == GL_TEXTURE_CUBE_MAP) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB : textarget, 0, GL_TEXTURE_INTERNAL_FORMAT, (GLint*)&internalFormat); if (handle_subtexture) glCopyTexSubImage2D (GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + target.subtexture, 0, 0, 0, orgX, orgY, txt_w, txt_h); else if (handle_3d) G3D->ext->glCopyTexSubImage3D (textarget, 0, 0, 0, orgX, orgY, target.subtexture, txt_w, txt_h); else glCopyTexSubImage2D (textarget, 0, 0, 0, orgX, orgY, txt_w, txt_h); } } tex_mm->RegenerateMipmaps(); } }
bool WrappedOpenGL::Serialise_wglDXLockObjectsNV(SerialiserType &ser, GLResource Resource) { SERIALISE_ELEMENT(Resource); SERIALISE_ELEMENT_LOCAL(textype, Resource.Namespace == eResBuffer ? eGL_NONE : m_Textures[GetResourceManager()->GetID(Resource)].curType) .Hidden(); const GLHookSet &gl = m_Real; // buffer contents are easier to save if(textype == eGL_NONE) { byte *Contents = NULL; uint32_t length = 1; // while writing, fetch the buffer's size and contents if(ser.IsWriting()) { gl.glGetNamedBufferParameterivEXT(Resource.name, eGL_BUFFER_SIZE, (GLint *)&length); Contents = new byte[length]; GLuint oldbuf = 0; gl.glGetIntegerv(eGL_COPY_READ_BUFFER_BINDING, (GLint *)&oldbuf); gl.glBindBuffer(eGL_COPY_READ_BUFFER, Resource.name); gl.glGetBufferSubData(eGL_COPY_READ_BUFFER, 0, (GLsizeiptr)length, Contents); gl.glBindBuffer(eGL_COPY_READ_BUFFER, oldbuf); } SERIALISE_ELEMENT_ARRAY(Contents, length); SERIALISE_CHECK_READ_ERRORS(); // restore on replay if(IsReplayingAndReading()) { uint32_t liveLength = 1; gl.glGetNamedBufferParameterivEXT(Resource.name, eGL_BUFFER_SIZE, (GLint *)&liveLength); gl.glNamedBufferSubData(Resource.name, 0, (GLsizeiptr)RDCMIN(length, liveLength), Contents); } } else { GLuint ppb = 0, pub = 0; PixelPackState pack; PixelUnpackState unpack; // save and restore pixel pack/unpack state. We only need one or the other but for clarity we // push and pop both always. if(ser.IsWriting() || !IsStructuredExporting(m_State)) { gl.glGetIntegerv(eGL_PIXEL_PACK_BUFFER_BINDING, (GLint *)&ppb); gl.glGetIntegerv(eGL_PIXEL_UNPACK_BUFFER_BINDING, (GLint *)&pub); gl.glBindBuffer(eGL_PIXEL_PACK_BUFFER, 0); gl.glBindBuffer(eGL_PIXEL_UNPACK_BUFFER, 0); pack.Fetch(&gl, false); unpack.Fetch(&gl, false); ResetPixelPackState(gl, false, 1); ResetPixelUnpackState(gl, false, 1); } TextureData &details = m_Textures[GetResourceManager()->GetID(Resource)]; GLuint tex = Resource.name; // serialise the metadata for convenience SERIALISE_ELEMENT_LOCAL(internalFormat, details.internalFormat).Hidden(); SERIALISE_ELEMENT_LOCAL(width, details.width).Hidden(); SERIALISE_ELEMENT_LOCAL(height, details.height).Hidden(); SERIALISE_ELEMENT_LOCAL(depth, details.depth).Hidden(); RDCASSERT(internalFormat == details.internalFormat, internalFormat, details.internalFormat); RDCASSERT(width == details.width, width, details.width); RDCASSERT(height == details.height, height, details.height); RDCASSERT(depth == details.depth, depth, details.depth); GLenum fmt = GetBaseFormat(internalFormat); GLenum type = GetDataType(internalFormat); GLint dim = details.dimension; uint32_t size = (uint32_t)GetByteSize(width, height, depth, fmt, type); int mips = 0; if(IsReplayingAndReading()) mips = GetNumMips(gl, textype, tex, width, height, depth); byte *scratchBuf = NULL; // on read and write, we allocate a single buffer big enough for all mips and re-use it // to avoid repeated new/free. scratchBuf = AllocAlignedBuffer(size); GLuint prevtex = 0; if(!IsStructuredExporting(m_State)) { gl.glGetIntegerv(TextureBinding(details.curType), (GLint *)&prevtex); gl.glBindTexture(textype, tex); } for(int i = 0; i < mips; i++) { int w = RDCMAX(details.width >> i, 1); int h = RDCMAX(details.height >> i, 1); int d = RDCMAX(details.depth >> i, 1); if(textype == eGL_TEXTURE_CUBE_MAP_ARRAY || textype == eGL_TEXTURE_1D_ARRAY || textype == eGL_TEXTURE_2D_ARRAY) d = details.depth; size = (uint32_t)GetByteSize(w, h, d, fmt, type); GLenum targets[] = { eGL_TEXTURE_CUBE_MAP_POSITIVE_X, eGL_TEXTURE_CUBE_MAP_NEGATIVE_X, eGL_TEXTURE_CUBE_MAP_POSITIVE_Y, eGL_TEXTURE_CUBE_MAP_NEGATIVE_Y, eGL_TEXTURE_CUBE_MAP_POSITIVE_Z, eGL_TEXTURE_CUBE_MAP_NEGATIVE_Z, }; int count = ARRAY_COUNT(targets); if(textype != eGL_TEXTURE_CUBE_MAP) { targets[0] = textype; count = 1; } for(int trg = 0; trg < count; trg++) { if(ser.IsWriting()) { // we avoid glGetTextureImageEXT as it seems buggy for cubemap faces gl.glGetTexImage(targets[trg], i, fmt, type, scratchBuf); } // serialise without allocating memory as we already have our scratch buf sized. ser.Serialise("SubresourceContents", scratchBuf, size, SerialiserFlags::NoFlags); if(IsReplayingAndReading() && !ser.IsErrored()) { if(dim == 1) gl.glTextureSubImage1DEXT(tex, targets[trg], i, 0, w, fmt, type, scratchBuf); else if(dim == 2) gl.glTextureSubImage2DEXT(tex, targets[trg], i, 0, 0, w, h, fmt, type, scratchBuf); else if(dim == 3) gl.glTextureSubImage3DEXT(tex, targets[trg], i, 0, 0, 0, w, h, d, fmt, type, scratchBuf); } } } FreeAlignedBuffer(scratchBuf); // restore pixel (un)packing state if(ser.IsWriting() || !IsStructuredExporting(m_State)) { gl.glBindBuffer(eGL_PIXEL_PACK_BUFFER, ppb); gl.glBindBuffer(eGL_PIXEL_UNPACK_BUFFER, pub); pack.Apply(&gl, false); unpack.Apply(&gl, false); } if(!IsStructuredExporting(m_State)) gl.glBindTexture(textype, prevtex); SERIALISE_CHECK_READ_ERRORS(); } return true; }