private void BuildMipmaps(PixelBox data) { int width, height, logW, logH, level; PixelBox scaled = data; scaled.Data = data.Data; scaled.Left = data.Left; scaled.Right = data.Right; scaled.Top = data.Top; scaled.Bottom = data.Bottom; scaled.Front = data.Front; scaled.Back = data.Back; width = data.Width; height = data.Height; logW = (int)System.Math.Log(width); logH = (int)System.Math.Log(height); level = (logW > logH ? logW : logH); for (int mip = 0; mip < level; mip++) { All glFormat = GLES2PixelUtil.GetGLOriginFormat(scaled.Format); All dataType = GLES2PixelUtil.GetGLOriginDataType(scaled.Format); GL.TexImage2D(this.faceTarget, mip, (int)glFormat, width, height, 0, glFormat, dataType, scaled.Data.Pin()); GLES2Config.GlCheckError(this); if (mip != 0) { scaled.Data = null; } if (width > 1) { width /= 2; } if (height > 1) { height /= 2; } int sizeInBytes = PixelUtil.GetMemorySize(width, height, 1, data.Format); scaled = new PixelBox(width, height, 1, data.Format); scaled.Data = BufferBase.Wrap(new byte[sizeInBytes]); Image.Scale(data, scaled, ImageFilter.Linear); } //Delete the scaled data for the last level if (level > 0) { scaled.Data = null; } }
public override void BlitFromMemory(Media.PixelBox src, Media.BasicBox dstBox) { if (this.Buffer.Contains(dstBox) == false) { throw new ArgumentOutOfRangeException("dstBox", "Destination box out of range"); } PixelBox scaled; if (src.Width != dstBox.Width || src.Height != dstBox.Height || src.Depth != dstBox.Depth) { //Scale to destination size //This also does pixel format conversion if needed this.AllocateBuffer(); scaled = this.Buffer.GetSubVolume(dstBox); Image.Scale(src, scaled, ImageFilter.Bilinear); } else if ((src.Format != format) || ((GLES2PixelUtil.GetGLOriginFormat(src.Format) == 0) && (src.Format != PixelFormat.R8G8B8))) { //Extents match, but format is not accepted as valid source format for GL //do conversion in temporary buffer this.AllocateBuffer(); scaled = this.Buffer.GetSubVolume(dstBox); PixelConverter.BulkPixelConversion(src, scaled); if (src.Format == PixelFormat.A4R4G4B4) { // ARGB->BGRA GLES2PixelUtil.ConvertToGLFormat(ref scaled, ref scaled); } } else { this.AllocateBuffer(); scaled = src.Clone(); if (src.Format == PixelFormat.R8G8B8) { scaled.Format = PixelFormat.B8G8R8; PixelConverter.BulkPixelConversion(src, scaled); } } this.Upload(scaled, dstBox); this.FreeBuffer(); }
public static void DoImageIO(string name, string group, string ext, ref List <Image> images, Resource r) { int imgIdx = images.Count; images.Add(new Image()); var dstream = ResourceGroupManager.Instance.OpenResource(name, group, true, r); images[imgIdx] = Image.FromStream(dstream, ext); int w = 0, h = 0; //Scale to nearest power 2 w = GLES2PixelUtil.OptionalPO2(images[imgIdx].Width); h = GLES2PixelUtil.OptionalPO2(images[imgIdx].Height); if ((images[imgIdx].Width != w) || (images[imgIdx].Height != h)) { images[imgIdx].Resize(w, h); } }
public GLES2RenderBuffer(All format, int width, int height, int numSamples) : base(width, height, 1, GLES2PixelUtil.GetClosestAxiomFormat(format, (All)PixelFormat.A8R8G8B8), BufferUsage.WriteOnly) { GlInternalFormat = format; //Genearte renderbuffer GL.GenRenderbuffers(1, ref this.renderBufferID); GLES2Config.GlCheckError(this); //Bind it to FBO GL.BindRenderbuffer(All.Renderbuffer, this.renderBufferID); GLES2Config.GlCheckError(this); //Allocate storage for depth buffer if (numSamples > 0) { } else { GL.RenderbufferStorage(All.Renderbuffer, format, width, height); GLES2Config.GlCheckError(this); } }
protected override void Download(PixelBox data) { #if GL_NV_get_tex_image if (data.Width != Width || data.Height != Height || data.Depth != Depth) { throw new Core.AxiomException("only download of entire buffer is supported by GL"); } GL.BindTexture(this.target, this.textureID); if (PixelUtil.IsCompressed(data.Format)) { if (data.Format != format || !data.IsConsecutive) { throw new Core.AxiomException("Compressed images must be consecutive, in the source format"); } GL.GetCompressedTexImageNV(this.faceTarget, this.level, data.Data); } else { if ((data.Width * PixelUtil.GetNumElemBytes(data.Format) & 3) != 0) { //Standard alignment of 4 is not right GL.PixelStore(Glenum.PackAlignment, 1); } //We can only get the entire texture GL.GetTexImageNV(this.faceTarget, this.level, GLES2PixelUtil.GetGLOriginFormat(data.Format), GLES2PixelUtil.GetGLOriginDataType(data.Format), data.Data); //Restore defaults GL.PixelStore(Glenum.PackAlignment, 4); } #else throw new Core.AxiomException("Downloading texture buffers is not supported by OpenGL ES"); #endif }
/// <summary> /// Internal method, create GLHardwarePixelBuffers for every face and mipmap level. /// This method must be called after the GL texture object was created, the number of mipmaps /// was set (Axiom.Configuration.Config.MaxTextureLayers) and glTexImageXD was called /// to allocate the buffer /// </summary>te private void CreateSurfaceList() { this.surfaceList.Clear(); //For all faces and mipmaps, store surfaces as HardwarePixelBuffer bool wantGeneratedMips = (usage & TextureUsage.AutoMipMap) != 0; // Do mipmapping in software? (uses GLU) For some cards, this is still needed. Of course, // only when mipmap generation is desired. bool doSoftware = wantGeneratedMips && !mipmapsHardwareGenerated && MipmapCount > 0; for (int face = 0; face < FaceCount; face++) { int width = Width; int height = Height; for (int mip = 0; mip <= MipmapCount; mip++) { GLES2HardwarePixelBuffer buf = new GLES2TextureBuffer(_name, this.GLES2TextureTarget, this.textureID, width, height, GLES2PixelUtil.GetClosestGLInternalFormat(format, hwGamma), (GLenum)GLES2PixelUtil.GetGLOriginDataType(format), face, mip, (BufferUsage)(usage), doSoftware && mip == 0, hwGamma, fsaa); this.surfaceList.Add(buf); //check for error if (buf.Width == 0 || buf.Height == 0 || buf.Depth == 0) { throw new AxiomException("Zero sized texture surface on texture " + Name + " face " + face.ToString() + " mipmap " + mip.ToString() + ". The GL driver probably refused to create the texture."); } } } }
protected override void createInternalResources() { //Conver to nearest power of two size if require this.width = GLES2PixelUtil.OptionalPO2(Width); this.height = GLES2PixelUtil.OptionalPO2(Height); this.depth = GLES2PixelUtil.OptionalPO2(Depth); //Adjust format if required format = TextureManager.Instance.GetNativeFormat(textureType, Format, usage); //Check requested number of mipmaps int maxMips = GLES2PixelUtil.GetMaxMipmaps(this.width, this.height, this.depth, format); if (PixelUtil.IsCompressed(format) && (mipmapCount == 0)) { requestedMipmapCount = 0; } mipmapCount = requestedMipmapCount; if (mipmapCount > maxMips) { mipmapCount = maxMips; } //Generate texture name GL.GenTextures(1, ref this.textureID); GLES2Config.GlCheckError(this); //Set texture name GL.BindTexture(this.GLES2TextureTarget, this.textureID); GLES2Config.GlCheckError(this); //Set some misc default parameters, tehse can of course be changed later GL.TexParameter(this.GLES2TextureTarget, GLenum.TextureMinFilter, (int)GLenum.Nearest); GLES2Config.GlCheckError(this); GL.TexParameter(this.GLES2TextureTarget, GLenum.TextureMagFilter, (int)GLenum.Nearest); GLES2Config.GlCheckError(this); GL.TexParameter(this.GLES2TextureTarget, GLenum.TextureWrapS, (int)GLenum.ClampToEdge); GLES2Config.GlCheckError(this); GL.TexParameter(this.GLES2TextureTarget, GLenum.TextureWrapT, (int)GLenum.ClampToEdge); GLES2Config.GlCheckError(this); //If we can do automip generation and the user desires this, do so mipmapsHardwareGenerated = Root.Instance.RenderSystem.Capabilities.HasCapability(Capabilities.HardwareMipMaps) && !PixelUtil.IsCompressed(format); //Ogre FIXME: For some reason this is crashing on iOS 5 #if !MONOTOUCH && ANDROID if ((usage & TextureUsage.AutoMipMap) == TextureUsage.AutoMipMap && requestedMipmapCount > 0 && mipmapsHardwareGenerated && (textureType != Graphics.TextureType.CubeMap)) { GL.GenerateMipmap(this.GLES2TextureTarget); GLES2Config.GlCheckError(this); } #endif //Allocate internal buffer so that glTexSubImageXD can be used //INternal format GLenum glformat = GLES2PixelUtil.GetClosestGLInternalFormat(format, hwGamma); GLenum dataType = GLES2PixelUtil.GetGLOriginDataType(format); int width = Width; int height = Height; int depth = Depth; if (PixelUtil.IsCompressed(format)) { //Compressed formats int size = PixelUtil.GetMemorySize(Width, Height, Depth, Format); // Provide temporary buffer filled with zeroes as glCompressedTexImageXD does not // accept a 0 pointer like normal glTexImageXD // Run through this process for every mipmap to pregenerate mipmap pyramid var tmpData = new IntPtr(); for (int mip = 0; mip < mipmapCount; mip++) { size = PixelUtil.GetMemorySize(width, height, depth, Format); switch (textureType) { case TextureType.OneD: case TextureType.TwoD: GL.CompressedTexImage2D(GLenum.Texture2D, mip, glformat, width, height, 0, size, tmpData); GLES2Config.GlCheckError(this); break; case TextureType.CubeMap: for (int face = 0; face < 6; face++) { GL.CompressedTexImage2D((GLenum)((int)GLenum.TextureCubeMapPositiveX + face), mip, glformat, width, height, 0, size, tmpData); GLES2Config.GlCheckError(this); } break; case TextureType.ThreeD: break; default: break; } if (width > 1) { width = width / 2; } if (height > 1) { height = height / 2; } if (depth > 1) { depth = depth / 2; } } tmpData = IntPtr.Zero; } else { //Run through this process to pregenerate mipmap pyramid for (int mip = 0; mip < mipmapCount; mip++) { //Normal formats switch (textureType) { case TextureType.OneD: case TextureType.TwoD: GL.TexImage2D(GLenum.Texture2D, mip, (int)glformat, width, height, 0, glformat, dataType, new IntPtr()); GLES2Config.GlCheckError(this); break; case TextureType.CubeMap: for (int face = 0; face < 6; face++) { GL.TexImage2D(GLenum.TextureCubeMapPositiveX + face, mip, (int)glformat, width, height, 0, glformat, dataType, new IntPtr()); GLES2Config.GlCheckError(this); } break; case TextureType.ThreeD: default: break; } if (width > 1) { width /= 2; } if (height > 1) { height /= 2; } } } this.CreateSurfaceList(); //Get final internal format base.format = this.GetBuffer(0, 0).Format; }
public GLES2TextureBuffer(string baseName, All target, int id, int width, int height, All internalFormat, All format, int face, int level, BufferUsage usage, bool crappyCard, bool writeGamma, int fsaa) : base(0, 0, 0, PixelFormat.Unknown, usage) { this.target = target; this.textureID = id; this.face = face; this.level = level; this.softwareMipmap = crappyCard; GLES2Config.GlCheckError(this); GL.BindTexture(target, this.textureID); GLES2Config.GlCheckError(this); //Get face identifier this.faceTarget = this.target; if (this.target == All.TextureCubeMap) { this.faceTarget = All.TextureCubeMapPositiveX + face; } //Calculate the width and height of the texture at this mip level this.width = this.level == 0 ? width : (int)(width / Math.Utility.Pow(2, level)); this.height = this.level == 0 ? height : (int)(height / Math.Utility.Pow(2, level)); if (this.width < 1) { this.width = 1; } if (this.height < 1) { this.height = 1; } //Only 2D is supporte so depth is always 1 depth = 1; GlInternalFormat = internalFormat; this.format = GLES2PixelUtil.GetClosestAxiomFormat(internalFormat, format); rowPitch = this.width; slicePitch = this.height * this.width; sizeInBytes = PixelUtil.GetMemorySize(this.width, this.height, depth, this.format); //Setup a pixel box Buffer = new PixelBox(this.width, this.height, depth, this.format); if (this.width == 0 || this.height == 0 || depth == 0) { //We are invalid, do not allocat a buffer return; } if (((TextureUsage)usage & TextureUsage.RenderTarget) == TextureUsage.RenderTarget) { //Create render target for each slice for (int zoffset = 0; zoffset < depth; zoffset++) { var name = "rtt/ " + GetHashCode() + "/" + baseName; var rtarget = new GLES2SurfaceDesc { buffer = this, zoffset = zoffset }; var trt = GLES2RTTManager.Instance.CreateRenderTexture(name, rtarget, writeGamma, fsaa); this.sliceTRT.Add(trt); Core.Root.Instance.RenderSystem.AttachRenderTarget(this.sliceTRT[zoffset]); } } }
public override void BlitFromMemory(PixelBox src, BasicBox 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 //Ogre TODO: Check that extension is NOT available if (PixelUtil.IsLuminance(src.Format) || PixelUtil.IsLuminance(this.format) || (src.Width == dstBox.Width && src.Height == dstBox.Height && src.Depth == dstBox.Depth)) { base.BlitFromMemory(src, dstBox); return; } if (!Buffer.Contains(dstBox)) { throw new ArgumentOutOfRangeException("dstBox", "Destination box out of range"); } //For scoped deletion of conversion buffer PixelBox srcPB; BufferBase buf; //first, convert the srcbox to a OpenGL compatible pixel format if (GLES2PixelUtil.GetGLOriginFormat(src.Format) == 0) { //Conver to buffer intenral format buf = BufferBase.Wrap(new byte[PixelUtil.GetMemorySize(src.Width, src.Height, src.Depth, this.format)]); srcPB = new PixelBox(src.Width, src.Height, src.Depth, this.format, buf); PixelConverter.BulkPixelConversion(src, srcPB); } else { //No conversion needed srcPB = src; } //Create temporary texture to store source data int id = 0; All target = All.Texture2D; int width = GLES2PixelUtil.OptionalPO2(src.Width); int height = GLES2PixelUtil.OptionalPO2(src.Height); All format = GLES2PixelUtil.GetClosestGLInternalFormat(src.Format); All datatype = GLES2PixelUtil.GetGLOriginDataType(src.Format); //Generate texture name GL.GenTextures(1, ref id); GLES2Config.GlCheckError(this); //Set texture type GL.BindTexture(target, id); GLES2Config.GlCheckError(this); //Allocate texture memory GL.TexImage2D(target, 0, (int)format, width, height, 0, format, datatype, IntPtr.Zero); GLES2Config.GlCheckError(this); var tex = new GLES2TextureBuffer(string.Empty, target, id, width, height, format, (All)src.Format, 0, 0, BufferUsage.StaticWriteOnly, false, false, 0); //Upload data to 0,0,0 in temprary texture var tempTarget = new BasicBox(0, 0, 0, srcPB.Width, srcPB.Height, srcPB.Depth); tex.Upload(srcPB, tempTarget); //Blit this.BlitFromTexture(tex, tempTarget, dstBox); GLES2Config.GlCheckError(this); }
protected override void Upload(PixelBox data, BasicBox dest) { GL.BindTexture(this.target, this.textureID); GLES2Config.GlCheckError(this); if (PixelUtil.IsCompressed(data.Format)) { if (data.Format != format || !data.IsConsecutive) { var glFormat = GLES2PixelUtil.GetClosestGLInternalFormat(format); //Data must be consecutive and at beginning of buffer as PixelStore is not allowed //for compressed formats if (dest.Left == 0 && dest.Top == 0) { GL.CompressedTexImage2D(this.faceTarget, this.level, glFormat, dest.Width, dest.Height, 0, data.ConsecutiveSize, data.Data.Pin()); GLES2Config.GlCheckError(this); } else { GL.CompressedTexSubImage2D(this.faceTarget, this.level, dest.Left, dest.Top, dest.Width, dest.Height, glFormat, data.ConsecutiveSize, data.Data.Pin()); GLES2Config.GlCheckError(this); } } } else if (this.softwareMipmap) { if (data.Width != data.RowPitch) { //Ogre TODO throw new Core.AxiomException("Unsupported texture format"); } if (data.Height * data.Width != data.SlicePitch) { //Ogre TODO throw new Core.AxiomException("Unsupported texture format"); } GL.PixelStore(All.UnpackAlignment, 1); GLES2Config.GlCheckError(this); this.BuildMipmaps(data); } else { if (data.Width != data.RowPitch) { //Ogre TODO throw new Core.AxiomException("Unsupported texture format"); } if (data.Height * data.Width != data.SlicePitch) { //Ogre TODO throw new Core.AxiomException("Unsupported texture format"); } if ((data.Width * PixelUtil.GetNumElemBytes(data.Format) & 3) != 0) { //Standard alignment of 4 is not right GL.PixelStore(All.UnpackAlignment, 1); GLES2Config.GlCheckError(this); } var dataPtr = data.Data.Pin(); GL.TexImage2D(this.faceTarget, this.level, (int)GLES2PixelUtil.GetClosestGLInternalFormat(data.Format), data.Width, data.Height, 0, GLES2PixelUtil.GetGLOriginFormat(data.Format), GLES2PixelUtil.GetGLOriginDataType(data.Format), dataPtr); //GL.TexSubImage2D( this.faceTarget, this.level, dest.Left, dest.Top, dest.Width, dest.Height, GLES2PixelUtil.GetGLOriginFormat( data.Format ), GLES2PixelUtil.GetGLOriginDataType( data.Format ), dataPtr ); data.Data.UnPin(); GLES2Config.GlCheckError(this); } GL.PixelStore(All.UnpackAlignment, 4); GLES2Config.GlCheckError(this); }
/// <summary> /// Detect which internal formats are allowed as RTT Also detect what combinations of stencil and depth are allowed with this interal format. /// </summary> private void DetectFBOFormats() { //Try all formats, and report which ones work as target int fb = 0, tid = 0; GLenum target = GLenum.Texture2D; for (int x = 0; x < (int)PixelFormat.Count; x++) { this.props[x].Valid = false; //Fetch gl format token var fmt = GLES2PixelUtil.GetGLInternalFormat((PixelFormat)x); if ((fmt == GLenum.None) && (x != 0)) { continue; } //No test for compressed formats if (PixelUtil.IsCompressed((PixelFormat)x)) { continue; } //Create and attach framebuffer GL.GenFramebuffers(1, ref fb); GL.BindFramebuffer(GLenum.Framebuffer, fb); if (fmt != GLenum.None) { //Create and attach texture GL.GenTextures(1, ref tid); GL.BindTexture(target, tid); //Set some default parameters GL.TexParameter(target, GLenum.TextureMinFilter, (int)GLenum.Nearest); GL.TexParameter(target, GLenum.TextureMagFilter, (int)GLenum.Nearest); GL.TexParameter(target, GLenum.TextureWrapS, (int)GLenum.ClampToEdge); GL.TexParameter(target, GLenum.TextureWrapT, (int)GLenum.ClampToEdge); GL.TexImage2D(target, 0, (int)fmt, PROBE_SIZE, PROBE_SIZE, 0, fmt, GLES2PixelUtil.GetGLOriginDataType((PixelFormat)x), IntPtr.Zero); GL.FramebufferTexture2D(GLenum.Framebuffer, GLenum.ColorAttachment0, target, tid, 0); } //Check status GLenum status = GL.CheckFramebufferStatus(GLenum.Framebuffer); // 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 == GLenum.None || status == GLenum.FramebufferComplete) { this.props[x].Valid = true; var sb = new StringBuilder(); sb.Append("FBO " + PixelUtil.GetFormatName((PixelFormat)x) + " depth/stencil support: "); //For each depth/stencil formats for (int depth = 0; depth < DepthFormatCount; depth++) { if (depthFormats[depth] != GLenum.Depth24Stencil8Oes) { //General depth/stencil combination for (int stencil = 0; stencil < StencilFormatCount; stencil++) { if (this.TryFormat(depthFormats[depth], stencilFormats[stencil])) { //Add mode to allowed modes sb.Append("D" + depthBits[depth] + "S" + stencilBits[stencil] + " "); var mode = new FormatProperties.Mode(); mode.Depth = depth; mode.Stencil = stencil; this.props[x].Modes.Add(mode); } } } else { //Packed depth/stencil format if (this.TryPackedFormat(depthFormats[depth])) { //Add mode to allowed modes sb.Append("Packed-D" + depthBits[depth] + "S" + 8 + " "); var mode = new FormatProperties.Mode(); mode.Depth = depth; mode.Stencil = 0; //unuse this.props[x].Modes.Add(mode); } } } Core.LogManager.Instance.Write(sb.ToString()); } //Delte texture and framebuffer GL.BindFramebuffer(GLenum.Framebuffer, 0); GL.DeleteFramebuffers(1, ref fb); if (fmt != GLenum.None) { GL.DeleteTextures(1, ref tid); } } // Clear any errors GLES2Config.GlClearError(); string fmtstring = string.Empty; for (int x = 0; x < (int)PixelFormat.Count; x++) { if (this.props[x].Valid) { fmtstring += PixelUtil.GetFormatName((PixelFormat)x) + " "; } Core.LogManager.Instance.Write("[GLES2] : Valid FBO targets " + fmtstring); } }
public override void BlitToMemory(Media.BasicBox srcBox, Media.PixelBox dst) { if (!this.Buffer.Contains(srcBox)) { throw new ArgumentOutOfRangeException("srcBox", "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 && GLES2PixelUtil.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 this.Download(dst); } else { //Use buffer for intermediate copy this.AllocateBuffer(); //Download entire buffer this.Download(this.Buffer); if (srcBox.Width != dst.Width || srcBox.Height != dst.Height || srcBox.Depth != dst.Depth) { //We need scaling Image.Scale(this.Buffer.GetSubVolume(srcBox), dst, ImageFilter.Bilinear); } else { //Just copy the bit that we need PixelConverter.BulkPixelConversion(this.Buffer.GetSubVolume(srcBox), dst); } this.FreeBuffer(); } }