public override void BlitToMemory(BasicBox srcBox, PixelBox dst) { if (!this._buffer.Contains(srcBox)) { throw new ArgumentException("Source box out of range."); } if (srcBox.Left == 0 && srcBox.Right == Width && srcBox.Top == 0 && srcBox.Bottom == Height && srcBox.Front == 0 && srcBox.Back == Depth && dst.Width == Width && dst.Height == Height && dst.Depth == Depth && GLPixelUtil.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(this._buffer); if (srcBox.Width != dst.Width || srcBox.Height != dst.Height || srcBox.Depth != dst.Depth) { //TODO Implement Image.Scale throw new Exception("Image scaling not yet implemented"); // We need scaling //Image.Scale( _buffer.GetSubVolume( srcBox ), dst, ImageFilter.BiLinear ); } else { // Just copy the bit that we need PixelConverter.BulkPixelConversion(this._buffer.GetSubVolume(srcBox), dst); } freeBuffer(); } }
public override void CopyContentsToMemory(PixelBox dst, FrameBuffer buffer) { if ((dst.Left < 0) || (dst.Right > Width) || (dst.Top < 0) || (dst.Bottom > Height) || (dst.Front != 0) || (dst.Back != 1)) { throw new Exception("Invalid box."); } if (buffer == RenderTarget.FrameBuffer.Auto) { buffer = IsFullScreen ? RenderTarget.FrameBuffer.Front : RenderTarget.FrameBuffer.Back; } int format = GLPixelUtil.GetGLOriginFormat(dst.Format); int type = GLPixelUtil.GetGLOriginDataType(dst.Format); if ((format == Gl.GL_NONE) || (type == 0)) { throw new Exception("Unsupported format."); } // Switch context if different from current one RenderSystem rsys = Root.Instance.RenderSystem; rsys.Viewport = this.GetViewport(0); // Must change the packing to ensure no overruns! Gl.glPixelStorei(Gl.GL_PACK_ALIGNMENT, 1); Gl.glReadBuffer((buffer == RenderTarget.FrameBuffer.Front) ? Gl.GL_FRONT : Gl.GL_BACK); Gl.glReadPixels(dst.Left, dst.Top, dst.Width, dst.Height, format, type, dst.Data); // restore default alignment Gl.glPixelStorei(Gl.GL_PACK_ALIGNMENT, 4); //vertical flip { int rowSpan = dst.Width * PixelUtil.GetNumElemBytes(dst.Format); int height = dst.Height; byte[] tmpData = new byte[rowSpan * height]; unsafe { var dataPtr = dst.Data.ToBytePointer(); //int *srcRow = (uchar *)dst.data, *tmpRow = tmpData + (height - 1) * rowSpan; for (int row = height - 1, tmpRow = 0; row >= 0; row--, tmpRow++) { for (int col = 0; col < rowSpan; col++) { tmpData[tmpRow * rowSpan + col] = dataPtr[row * rowSpan + col]; } } } var tmpDataHandle = BufferBase.Wrap(tmpData); Memory.Copy(tmpDataHandle, dst.Data, rowSpan * height); } }
public GLRenderBuffer(int format, int width, int height, int fsaa) : base(width, height, 1, GLPixelUtil.GetClosestPixelFormat(format), BufferUsage.WriteOnly) { GLFormat = format; /// Generate renderbuffer Gl.glGenRenderbuffersEXT(1, out this._renderBufferId); /// Bind it to FBO Gl.glBindRenderbufferEXT(Gl.GL_RENDERBUFFER_EXT, this._renderBufferId); /// Allocate storage for depth buffer Gl.glRenderbufferStorageEXT(Gl.GL_RENDERBUFFER_EXT, format, width, height); }
protected override void download(PixelBox data) { if (data.Width != Width || data.Height != Height || data.Depth != Depth) { throw new ArgumentException("only download of entire buffer is supported by GL"); } Gl.glBindTexture(this._target, this._textureId); if (PixelUtil.IsCompressed(data.Format)) { if (data.Format != Format || !data.IsConsecutive) { throw new ArgumentException("Compressed images must be consecutive, in the source format"); } // Data must be consecutive and at beginning of buffer as PixelStorei not allowed // for compressed formate Gl.glGetCompressedTexImageARB(this._faceTarget, this._level, data.Data.Pin()); data.Data.UnPin(); } else { if (data.Width != data.RowPitch) { Gl.glPixelStorei(Gl.GL_PACK_ROW_LENGTH, data.RowPitch); } if (data.Height * data.Width != data.SlicePitch) { Gl.glPixelStorei(Gl.GL_PACK_IMAGE_HEIGHT, (data.SlicePitch / data.Width)); } if (((data.Width * PixelUtil.GetNumElemBytes(data.Format)) & 3) != 0) { // Standard alignment of 4 is not right Gl.glPixelStorei(Gl.GL_PACK_ALIGNMENT, 1); } // We can only get the entire texture Gl.glGetTexImage(this._faceTarget, this._level, GLPixelUtil.GetGLOriginFormat(data.Format), GLPixelUtil.GetGLOriginDataType(data.Format), data.Data.Pin()); data.Data.UnPin(); // Restore defaults Gl.glPixelStorei(Gl.GL_PACK_ROW_LENGTH, 0); Gl.glPixelStorei(Gl.GL_PACK_IMAGE_HEIGHT, 0); Gl.glPixelStorei(Gl.GL_PACK_ALIGNMENT, 4); } }
public override void BlitFromMemory(PixelBox src, BasicBox dstBox) { PixelBox scaled; if (!this._buffer.Contains(dstBox)) { throw new ArgumentException("Destination box out of range."); } if (src.Width != dstBox.Width || src.Height != dstBox.Height || src.Depth != dstBox.Depth) { // Scale to destination size. Use DevIL and not iluScale because ILU screws up for // floating point textures and cannot cope with 3D images. // This also does pixel format conversion if needed allocateBuffer(); scaled = this._buffer.GetSubVolume(dstBox); Image.Scale(src, scaled, ImageFilter.Bilinear); } else if (GLPixelUtil.GetGLOriginFormat(src.Format) == 0) { // Extents match, but format is not accepted as valid source format for GL // do conversion in temporary buffer allocateBuffer(); scaled = this._buffer.GetSubVolume(dstBox); PixelConverter.BulkPixelConversion(src, scaled); } else { // No scaling or conversion needed scaled = src; // Set extents for upload scaled.Left = dstBox.Left; scaled.Right = dstBox.Right; scaled.Top = dstBox.Top; scaled.Bottom = dstBox.Bottom; scaled.Front = dstBox.Front; scaled.Back = dstBox.Back; } upload(scaled); freeBuffer(); }
public GLTextureBuffer(string baseName, int target, int id, int face, int level, BufferUsage usage, bool softwareMipmap, BaseGLSupport glSupport, bool writeGamma, int fsaa) : base(0, 0, 0, PixelFormat.Unknown, usage) { int value; this._glSupport = glSupport; this._target = target; this._textureId = id; this._face = face; this._level = level; this._softwareMipmap = softwareMipmap; Gl.glBindTexture(this._target, this._textureId); // Get face identifier this._faceTarget = this._target; if (this._target == Gl.GL_TEXTURE_CUBE_MAP) { this._faceTarget = Gl.GL_TEXTURE_CUBE_MAP_POSITIVE_X + this._face; } // Get width Gl.glGetTexLevelParameteriv(this._faceTarget, this._level, Gl.GL_TEXTURE_WIDTH, out value); width = value; // Get height if (this._target == Gl.GL_TEXTURE_1D) { value = 1; // Height always 1 for 1D textures } else { Gl.glGetTexLevelParameteriv(this._faceTarget, this._level, Gl.GL_TEXTURE_HEIGHT, out value); } height = value; // Get depth if (this._target != Gl.GL_TEXTURE_3D) { value = 1; // Depth always 1 for non-3D textures } else { Gl.glGetTexLevelParameteriv(this._faceTarget, this._level, Gl.GL_TEXTURE_DEPTH, out value); } depth = value; // Get format Gl.glGetTexLevelParameteriv(this._faceTarget, this._level, Gl.GL_TEXTURE_INTERNAL_FORMAT, out value); GLFormat = value; format = GLPixelUtil.GetClosestPixelFormat(value); // Default rowPitch = Width; slicePitch = Height * Width; sizeInBytes = PixelUtil.GetMemorySize(Width, Height, Depth, Format); // Set up pixel box buffer = new PixelBox(Width, Height, Depth, Format); if (Width == 0 || Height == 0 || Depth == 0) { /// We are invalid, do not allocate a buffer return; } // Is this a render target? if (((TextureUsage)Usage & TextureUsage.RenderTarget) == TextureUsage.RenderTarget) { // Create render target for each slice this._sliceTRT.Capacity = Depth; for (int zoffset = 0; zoffset < Depth; ++zoffset) { String name; name = String.Format("{0}/{1}/{2}/{3}", baseName, face, this._level, zoffset); GLSurfaceDesc renderTarget; renderTarget.Buffer = this; renderTarget.ZOffset = zoffset; RenderTexture trt = GLRTTManager.Instance.CreateRenderTexture(name, renderTarget, writeGamma, fsaa); this._sliceTRT.Add(trt); Root.Instance.RenderSystem.AttachRenderTarget(this._sliceTRT[zoffset]); } } }
protected override void upload(PixelBox box) { Gl.glBindTexture(this._target, this._textureId); if (PixelUtil.IsCompressed(box.Format)) { if (box.Format != Format || !box.IsConsecutive) { throw new ArgumentException("Compressed images must be consecutive, in the source format"); } int format = GLPixelUtil.GetClosestGLInternalFormat(Format); // Data must be consecutive and at beginning of buffer as PixelStorei not allowed // for compressed formats switch (this._target) { case Gl.GL_TEXTURE_1D: Gl.glCompressedTexSubImage1DARB(Gl.GL_TEXTURE_1D, this._level, box.Left, box.Width, format, box.ConsecutiveSize, box.Data.Pin()); box.Data.UnPin(); break; case Gl.GL_TEXTURE_2D: case Gl.GL_TEXTURE_CUBE_MAP: Gl.glCompressedTexSubImage2DARB(this._faceTarget, this._level, box.Left, box.Top, box.Width, box.Height, format, box.ConsecutiveSize, box.Data.Pin()); box.Data.UnPin(); break; case Gl.GL_TEXTURE_3D: Gl.glCompressedTexSubImage3DARB(Gl.GL_TEXTURE_3D, this._level, box.Left, box.Top, box.Front, box.Width, box.Height, box.Depth, format, box.ConsecutiveSize, box.Data.Pin()); box.Data.UnPin(); break; } } else if (this._softwareMipmap) { int internalFormat; Gl.glGetTexLevelParameteriv(this._target, this._level, Gl.GL_TEXTURE_INTERNAL_FORMAT, out internalFormat); if (box.Width != box.RowPitch) { Gl.glPixelStorei(Gl.GL_UNPACK_ROW_LENGTH, box.RowPitch); } if (box.Height * box.Width != box.SlicePitch) { Gl.glPixelStorei(Gl.GL_UNPACK_IMAGE_HEIGHT, (box.SlicePitch / box.Width)); } Gl.glPixelStorei(Gl.GL_UNPACK_ALIGNMENT, 1); switch (this._target) { case Gl.GL_TEXTURE_1D: Glu.gluBuild1DMipmaps(Gl.GL_TEXTURE_1D, internalFormat, box.Width, GLPixelUtil.GetGLOriginFormat(box.Format), GLPixelUtil.GetGLOriginDataType(box.Format), box.Data.Pin()); box.Data.UnPin(); break; case Gl.GL_TEXTURE_2D: case Gl.GL_TEXTURE_CUBE_MAP: Glu.gluBuild2DMipmaps(this._faceTarget, internalFormat, box.Width, box.Height, GLPixelUtil.GetGLOriginFormat(box.Format), GLPixelUtil.GetGLOriginDataType(box.Format), box.Data.Pin()); box.Data.UnPin(); break; case Gl.GL_TEXTURE_3D: /* Requires GLU 1.3 which is harder to come by than cards doing hardware mipmapping * Most 3D textures don't need mipmaps? * Gl.gluBuild3DMipmaps( * Gl.GL_TEXTURE_3D, internalFormat, * box.getWidth(), box.getHeight(), box.getDepth(), * GLPixelUtil.getGLOriginFormat(box.format), GLPixelUtil.getGLOriginDataType(box.format), * box.box); */ Gl.glTexImage3D(Gl.GL_TEXTURE_3D, 0, internalFormat, box.Width, box.Height, box.Depth, 0, GLPixelUtil.GetGLOriginFormat(box.Format), GLPixelUtil.GetGLOriginDataType(box.Format), box.Data.Pin()); box.Data.UnPin(); break; } } else { if (box.Width != box.RowPitch) { Gl.glPixelStorei(Gl.GL_UNPACK_ROW_LENGTH, box.RowPitch); } if (box.Height * box.Width != box.SlicePitch) { Gl.glPixelStorei(Gl.GL_UNPACK_IMAGE_HEIGHT, (box.SlicePitch / box.Width)); } if (((box.Width * PixelUtil.GetNumElemBytes(box.Format)) & 3) != 0) { // Standard alignment of 4 is not right Gl.glPixelStorei(Gl.GL_UNPACK_ALIGNMENT, 1); } switch (this._target) { case Gl.GL_TEXTURE_1D: Gl.glTexSubImage1D(Gl.GL_TEXTURE_1D, this._level, box.Left, box.Width, GLPixelUtil.GetGLOriginFormat(box.Format), GLPixelUtil.GetGLOriginDataType(box.Format), box.Data.Pin()); box.Data.UnPin(); break; case Gl.GL_TEXTURE_2D: case Gl.GL_TEXTURE_CUBE_MAP: Gl.glTexSubImage2D(this._faceTarget, this._level, box.Left, box.Top, box.Width, box.Height, GLPixelUtil.GetGLOriginFormat(box.Format), GLPixelUtil.GetGLOriginDataType(box.Format), box.Data.Pin()); box.Data.UnPin(); break; case Gl.GL_TEXTURE_3D: Gl.glTexSubImage3D(Gl.GL_TEXTURE_3D, this._level, box.Left, box.Top, box.Front, box.Width, box.Height, box.Depth, GLPixelUtil.GetGLOriginFormat(box.Format), GLPixelUtil.GetGLOriginDataType(box.Format), box.Data.Pin()); box.Data.UnPin(); break; } } // Restore defaults Gl.glPixelStorei(Gl.GL_UNPACK_ROW_LENGTH, 0); Gl.glPixelStorei(Gl.GL_UNPACK_IMAGE_HEIGHT, 0); Gl.glPixelStorei(Gl.GL_UNPACK_ALIGNMENT, 4); }
/// <summary> /// Detect allowed FBO formats /// </summary> private void _detectFBOFormats() { // Try all formats, and report which ones work as target int fb, tid; int old_drawbuffer, old_readbuffer; int target = Gl.GL_TEXTURE_2D; Gl.glGetIntegerv(Gl.GL_DRAW_BUFFER, out old_drawbuffer); Gl.glGetIntegerv(Gl.GL_READ_BUFFER, out old_readbuffer); for (int x = 0; x < (int)PixelFormat.Count; ++x) { this._props[x].Valid = false; // Fetch GL format token int fmt = GLPixelUtil.GetGLInternalFormat((PixelFormat)x); if (fmt == Gl.GL_NONE && x != 0) { continue; } // No test for compressed formats if (PixelUtil.IsCompressed((PixelFormat)x)) { continue; } // Buggy ATI cards *crash* on non-RGB(A) formats int[] depths = PixelUtil.GetBitDepths((PixelFormat)x); if (fmt != Gl.GL_NONE && this._atiMode && (depths[0] == 0 || depths[1] == 0 || depths[2] == 0)) { continue; } // Buggy NVidia Drivers fail on 32Bit FP formats on Windows. if (PixelUtil.IsFloatingPoint((PixelFormat)x) && PlatformManager.IsWindowsOS && !this._atiMode) { continue; } // Create and attach framebuffer Gl.glGenFramebuffersEXT(1, out fb); Gl.glBindFramebufferEXT(Gl.GL_FRAMEBUFFER_EXT, fb); if (fmt != Gl.GL_NONE) { // Create and attach texture Gl.glGenTextures(1, out tid); Gl.glBindTexture(target, tid); // Set some default parameters so it won't fail on NVidia cards Gl.glTexParameteri(target, Gl.GL_TEXTURE_MAX_LEVEL, 0); Gl.glTexParameteri(target, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST); Gl.glTexParameteri(target, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_NEAREST); Gl.glTexParameteri(target, Gl.GL_TEXTURE_WRAP_S, Gl.GL_CLAMP_TO_EDGE); Gl.glTexParameteri(target, Gl.GL_TEXTURE_WRAP_T, Gl.GL_CLAMP_TO_EDGE); Gl.glTexImage2D(target, 0, fmt, PROBE_SIZE, PROBE_SIZE, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, IntPtr.Zero); Gl.glFramebufferTexture2DEXT(Gl.GL_FRAMEBUFFER_EXT, Gl.GL_COLOR_ATTACHMENT0_EXT, target, tid, 0); } else { // Draw to nowhere -- stencil/depth only tid = 0; Gl.glDrawBuffer(Gl.GL_NONE); Gl.glReadBuffer(Gl.GL_NONE); } // Check status int status = Gl.glCheckFramebufferStatusEXT(Gl.GL_FRAMEBUFFER_EXT); // Ignore status in case of fmt==GL_NONE, because no implementation will accept // a buffer without *any* attachment. Buffers with only stencil and depth attachment // might still be supported, so we must continue probing. if (fmt == Gl.GL_NONE || status == Gl.GL_FRAMEBUFFER_COMPLETE_EXT) { this._props[x].Valid = true; var str = new StringBuilder(); str.AppendFormat("\tFBO {0} depth/stencil support: ", PixelUtil.GetFormatName((PixelFormat)x)); // For each depth/stencil formats for (int depth = 0; depth < this._depthFormats.GetLength(0); ++depth) { if (this._depthFormats[depth] != GL_DEPTH24_STENCIL8_EXT) { // General depth/stencil combination for (int stencil = 0; stencil < this._stencilFormats.GetLength(0); ++stencil) { //LogManager.Instance.Write( "Trying {0} D{1}S{2} ", PixelUtil.GetFormatName( (PixelFormat)x ), _depthBits[ depth ], _stencilBits[ stencil ] ); if (_tryFormat(this._depthFormats[depth], this._stencilFormats[stencil])) { /// Add mode to allowed modes str.AppendFormat("D{0}S{1} ", this._depthBits[depth], this._stencilBits[stencil]); FormatProperties.Mode mode; mode.Depth = depth; mode.Stencil = stencil; this._props[x].Modes.Add(mode); } } } else { // Packed depth/stencil format #if false // Only query packed depth/stencil formats for 32-bit // non-floating point formats (ie not R32!) // Linux nVidia driver segfaults if you query others if (!PlatformManager.IsWindowsOS && (PixelUtil.GetNumElemBits((PixelFormat)x) != 32 || PixelUtil.IsFloatingPoint((PixelFormat)x))) { continue; } #endif if (_tryPackedFormat(this._depthFormats[depth])) { /// Add mode to allowed modes str.AppendFormat("Packed-D{0}S8 ", this._depthBits[depth]); FormatProperties.Mode mode; mode.Depth = depth; mode.Stencil = 0; // unuse this._props[x].Modes.Add(mode); } } } LogManager.Instance.Write(str.ToString()); } // Delete texture and framebuffer Gl.glBindFramebufferEXT(Gl.GL_FRAMEBUFFER_EXT, 0); Gl.glDeleteFramebuffersEXT(1, ref fb); // Workaround for NVIDIA / Linux 169.21 driver problem // see http://www.ogre3d.org/phpBB2/viewtopic.php?t=38037&start=25 Gl.glFinish(); Gl.glDeleteTextures(1, ref tid); } // It seems a bug in nVidia driver: glBindFramebufferEXT should restore // draw and read buffers, but in some unclear circumstances it won't. Gl.glDrawBuffer(old_drawbuffer); Gl.glReadBuffer(old_readbuffer); string fmtstring = ""; for (int x = 0; x < (int)PixelFormat.Count; ++x) { if (this._props[x].Valid) { fmtstring += PixelUtil.GetFormatName((PixelFormat)x) + " "; } } LogManager.Instance.Write("[GL] : Valid FBO targets " + fmtstring); }
protected override void createInternalResources() { // Convert to nearest power-of-two size if required Width = GLPixelUtil.OptionalPO2(Width); Height = GLPixelUtil.OptionalPO2(Height); Depth = GLPixelUtil.OptionalPO2(Depth); // Adjust format if required this.format = TextureManager.Instance.GetNativeFormat(TextureType, Format, Usage); // Check requested number of mipmaps int maxMips = GLPixelUtil.GetMaxMipmaps(Width, Height, Depth, Format); MipmapCount = requestedMipmapCount; if (MipmapCount > maxMips) { MipmapCount = maxMips; } // Generate texture name Gl.glGenTextures(1, out this._glTextureID); // Set texture type Gl.glBindTexture(GLTextureType, this._glTextureID); // This needs to be set otherwise the texture doesn't get rendered Gl.glTexParameteri(GLTextureType, Gl.GL_TEXTURE_MAX_LEVEL, MipmapCount); // Set some misc default parameters so NVidia won't complain, these can of course be changed later Gl.glTexParameteri(GLTextureType, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST); Gl.glTexParameteri(GLTextureType, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_NEAREST); Gl.glTexParameteri(GLTextureType, Gl.GL_TEXTURE_WRAP_S, Gl.GL_CLAMP_TO_EDGE); Gl.glTexParameteri(GLTextureType, Gl.GL_TEXTURE_WRAP_T, Gl.GL_CLAMP_TO_EDGE); // If we can do automip generation and the user desires this, do so mipmapsHardwareGenerated = Root.Instance.RenderSystem.Capabilities.HasCapability(Capabilities.HardwareMipMaps); if (((Usage & TextureUsage.AutoMipMap) == TextureUsage.AutoMipMap) && requestedMipmapCount != 0 && MipmapsHardwareGenerated) { Gl.glTexParameteri(GLTextureType, Gl.GL_GENERATE_MIPMAP, Gl.GL_TRUE); } // Allocate internal buffer so that glTexSubImageXD can be used // Internal format int format = GLPixelUtil.GetClosestGLInternalFormat(Format); int width = Width; int height = Height; int depth = Depth; { // Run through this process to pregenerate mipmap pyramid for (int mip = 0; mip <= MipmapCount; mip++) { // Normal formats switch (TextureType) { case TextureType.OneD: Gl.glTexImage1D(Gl.GL_TEXTURE_1D, mip, format, width, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, IntPtr.Zero); break; case TextureType.TwoD: Gl.glTexImage2D(Gl.GL_TEXTURE_2D, mip, format, width, height, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, IntPtr.Zero); break; case TextureType.ThreeD: Gl.glTexImage3D(Gl.GL_TEXTURE_3D, mip, format, width, height, depth, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, IntPtr.Zero); break; case TextureType.CubeMap: for (int face = 0; face < 6; face++) { Gl.glTexImage2D(Gl.GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mip, format, width, height, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, IntPtr.Zero); } break; } ; if (width > 1) { width = width / 2; } if (height > 1) { height = height / 2; } if (depth > 1) { depth = depth / 2; } } } _createSurfaceList(); // Get final internal format this.format = GetBuffer(0, 0).Format; }