public void DrawArrays(int first, int count, GalPrimitiveType primType) { if (count == 0) { return; } if (primType == GalPrimitiveType.Quads) { for (int offset = 0; offset < count; offset += 4) { GL.DrawArrays(PrimitiveType.TriangleFan, first + offset, 4); } } else if (primType == GalPrimitiveType.QuadStrip) { GL.DrawArrays(PrimitiveType.TriangleFan, first, 4); for (int offset = 2; offset < count; offset += 2) { GL.DrawArrays(PrimitiveType.TriangleFan, first + offset, 4); } } else { GL.DrawArrays(OglEnumConverter.GetPrimitiveType(primType), first, count); } }
private void SetAllBlendState(BlendState New) { Enable(EnableCap.Blend, New.Enabled); if (New.Enabled) { if (New.SeparateAlpha) { GL.BlendEquationSeparate( OglEnumConverter.GetBlendEquation(New.EquationRgb), OglEnumConverter.GetBlendEquation(New.EquationAlpha)); GL.BlendFuncSeparate( (BlendingFactorSrc)OglEnumConverter.GetBlendFactor(New.FuncSrcRgb), (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstRgb), (BlendingFactorSrc)OglEnumConverter.GetBlendFactor(New.FuncSrcAlpha), (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstAlpha)); } else { GL.BlendEquation(OglEnumConverter.GetBlendEquation(New.EquationRgb)); GL.BlendFunc( OglEnumConverter.GetBlendFactor(New.FuncSrcRgb), OglEnumConverter.GetBlendFactor(New.FuncDstRgb)); } } }
public void SetIndexArray(int size, GalIndexFormat format) { _indexBuffer.Type = OglEnumConverter.GetDrawElementsType(format); _indexBuffer.Count = size >> (int)format; _indexBuffer.ElemSizeLog2 = (int)format; }
public void Compile() { if (Handle == 0) { Handle = GL.CreateShader(OglEnumConverter.GetShaderType(Type)); CompileAndCheck(Handle, Code); } }
public void Reinterpret(long key, GalImage newImage) { if (!_texture.TryGetImage(key, out GalImage oldImage)) { return; } if (newImage.Format == oldImage.Format && newImage.Width == oldImage.Width && newImage.Height == oldImage.Height && newImage.Depth == oldImage.Depth && newImage.LayerCount == oldImage.LayerCount && newImage.TextureTarget == oldImage.TextureTarget) { return; } if (_copyPbo == 0) { _copyPbo = GL.GenBuffer(); } GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPbo); // The buffer should be large enough to hold the largest texture. int bufferSize = Math.Max(ImageUtils.GetSize(oldImage), ImageUtils.GetSize(newImage)); GL.BufferData(BufferTarget.PixelPackBuffer, bufferSize, IntPtr.Zero, BufferUsageHint.StreamCopy); if (!_texture.TryGetImageHandler(key, out ImageHandler cachedImage)) { throw new InvalidOperationException(); } (_, PixelFormat format, PixelType type) = OglEnumConverter.GetImageFormat(cachedImage.Format); TextureTarget target = ImageUtils.GetTextureTarget(newImage.TextureTarget); GL.BindTexture(target, cachedImage.Handle); GL.GetTexImage(target, 0, format, type, IntPtr.Zero); GL.BindBuffer(BufferTarget.PixelPackBuffer, 0); GL.BindBuffer(BufferTarget.PixelUnpackBuffer, _copyPbo); GL.PixelStore(PixelStoreParameter.UnpackRowLength, oldImage.Width); _texture.Create(key, ImageUtils.GetSize(newImage), newImage); GL.PixelStore(PixelStoreParameter.UnpackRowLength, 0); GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0); }
private void SetBlendState(int index, BlendState New, BlendState old) { if (New.Enabled != old.Enabled) { Enable(IndexedEnableCap.Blend, index, New.Enabled); } if (New.Enabled) { if (New.SeparateAlpha) { if (New.EquationRgb != old.EquationRgb || New.EquationAlpha != old.EquationAlpha) { GL.BlendEquationSeparate( index, OglEnumConverter.GetBlendEquation(New.EquationRgb), OglEnumConverter.GetBlendEquation(New.EquationAlpha)); } if (New.FuncSrcRgb != old.FuncSrcRgb || New.FuncDstRgb != old.FuncDstRgb || New.FuncSrcAlpha != old.FuncSrcAlpha || New.FuncDstAlpha != old.FuncDstAlpha) { GL.BlendFuncSeparate( index, (BlendingFactorSrc)OglEnumConverter.GetBlendFactor(New.FuncSrcRgb), (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstRgb), (BlendingFactorSrc)OglEnumConverter.GetBlendFactor(New.FuncSrcAlpha), (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstAlpha)); } } else { if (New.EquationRgb != old.EquationRgb) { GL.BlendEquation(index, OglEnumConverter.GetBlendEquation(New.EquationRgb)); } if (New.FuncSrcRgb != old.FuncSrcRgb || New.FuncDstRgb != old.FuncDstRgb) { GL.BlendFunc( index, (BlendingFactorSrc)OglEnumConverter.GetBlendFactor(New.FuncSrcRgb), (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstRgb)); } } } }
public void Bind(long key, int index, GalImage image) { if (_textureCache.TryGetValue(key, out ImageHandler cachedImage)) { GL.ActiveTexture(TextureUnit.Texture0 + index); TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget); GL.BindTexture(target, cachedImage.Handle); int[] swizzleRgba = new int[] { (int)OglEnumConverter.GetTextureSwizzle(image.XSource), (int)OglEnumConverter.GetTextureSwizzle(image.YSource), (int)OglEnumConverter.GetTextureSwizzle(image.ZSource), (int)OglEnumConverter.GetTextureSwizzle(image.WSource) }; GL.TexParameter(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba); } }
public void SetSampler(GalImage image, GalTextureSampler sampler) { int wrapS = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressU); int wrapT = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressV); int wrapR = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressP); int minFilter = (int)OglEnumConverter.GetTextureMinFilter(sampler.MinFilter, sampler.MipFilter); int magFilter = (int)OglEnumConverter.GetTextureMagFilter(sampler.MagFilter); TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget); GL.TexParameter(target, TextureParameterName.TextureWrapS, wrapS); GL.TexParameter(target, TextureParameterName.TextureWrapT, wrapT); GL.TexParameter(target, TextureParameterName.TextureWrapR, wrapR); GL.TexParameter(target, TextureParameterName.TextureMinFilter, minFilter); GL.TexParameter(target, TextureParameterName.TextureMagFilter, magFilter); float[] color = new float[] { sampler.BorderColor.Red, sampler.BorderColor.Green, sampler.BorderColor.Blue, sampler.BorderColor.Alpha }; GL.TexParameter(target, TextureParameterName.TextureBorderColor, color); if (sampler.DepthCompare) { GL.TexParameter(target, TextureParameterName.TextureCompareMode, (int)All.CompareRToTexture); GL.TexParameter(target, TextureParameterName.TextureCompareFunc, (int)OglEnumConverter.GetDepthCompareFunc(sampler.DepthCompareFunc)); } else { GL.TexParameter(target, TextureParameterName.TextureCompareMode, (int)All.None); GL.TexParameter(target, TextureParameterName.TextureCompareFunc, (int)All.Never); } }
public void DrawElements(long iboKey, int first, int vertexBase, GalPrimitiveType primType) { if (!_iboCache.TryGetValue(iboKey, out int iboHandle)) { return; } PrimitiveType mode = OglEnumConverter.GetPrimitiveType(primType); GL.BindBuffer(BufferTarget.ElementArrayBuffer, iboHandle); first <<= _indexBuffer.ElemSizeLog2; if (vertexBase != 0) { IntPtr indices = new IntPtr(first); GL.DrawElementsBaseVertex(mode, _indexBuffer.Count, _indexBuffer.Type, indices, vertexBase); } else { GL.DrawElements(mode, _indexBuffer.Count, _indexBuffer.Type, first); } }
public void Create(long key, int size, GalImage image) { int handle = GL.GenTexture(); TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget); GL.BindTexture(target, handle); const int level = 0; //TODO: Support mipmap textures. const int border = 0; _textureCache.AddOrUpdate(key, new ImageHandler(handle, image), (uint)size); if (ImageUtils.IsCompressed(image.Format)) { throw new InvalidOperationException("Surfaces with compressed formats are not supported!"); } (PixelInternalFormat internalFmt, PixelFormat format, PixelType type) = OglEnumConverter.GetImageFormat(image.Format); switch (target) { case TextureTarget.Texture1D: GL.TexImage1D( target, level, internalFmt, image.Width, border, format, type, IntPtr.Zero); break; case TextureTarget.Texture2D: GL.TexImage2D( target, level, internalFmt, image.Width, image.Height, border, format, type, IntPtr.Zero); break; case TextureTarget.Texture3D: GL.TexImage3D( target, level, internalFmt, image.Width, image.Height, image.Depth, border, format, type, IntPtr.Zero); break; case TextureTarget.Texture2DArray: GL.TexImage3D( target, level, internalFmt, image.Width, image.Height, image.LayerCount, border, format, type, IntPtr.Zero); break; default: throw new NotImplementedException($"Unsupported texture target type: {target}"); } }
public void Create(long key, byte[] data, GalImage image) { int handle = GL.GenTexture(); TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget); GL.BindTexture(target, handle); const int level = 0; //TODO: Support mipmap textures. const int border = 0; _textureCache.AddOrUpdate(key, new ImageHandler(handle, image), (uint)data.Length); if (ImageUtils.IsCompressed(image.Format) && !IsAstc(image.Format)) { InternalFormat internalFmt = OglEnumConverter.GetCompressedImageFormat(image.Format); switch (target) { case TextureTarget.Texture1D: GL.CompressedTexImage1D( target, level, internalFmt, image.Width, border, data.Length, data); break; case TextureTarget.Texture2D: GL.CompressedTexImage2D( target, level, internalFmt, image.Width, image.Height, border, data.Length, data); break; case TextureTarget.Texture3D: GL.CompressedTexImage3D( target, level, internalFmt, image.Width, image.Height, image.Depth, border, data.Length, data); break; // Cube map arrays are just 2D texture arrays with 6 entries // per cube map so we can handle them in the same way case TextureTarget.TextureCubeMapArray: case TextureTarget.Texture2DArray: GL.CompressedTexImage3D( target, level, internalFmt, image.Width, image.Height, image.LayerCount, border, data.Length, data); break; case TextureTarget.TextureCubeMap: Span <byte> array = new Span <byte>(data); int faceSize = ImageUtils.GetSize(image) / 6; for (int Face = 0; Face < 6; Face++) { GL.CompressedTexImage2D( TextureTarget.TextureCubeMapPositiveX + Face, level, internalFmt, image.Width, image.Height, border, faceSize, array.Slice(Face * faceSize, faceSize).ToArray()); } break; default: throw new NotImplementedException($"Unsupported texture target type: {target}"); } } else { //TODO: Use KHR_texture_compression_astc_hdr when available if (IsAstc(image.Format)) { int textureBlockWidth = ImageUtils.GetBlockWidth(image.Format); int textureBlockHeight = ImageUtils.GetBlockHeight(image.Format); int textureBlockDepth = ImageUtils.GetBlockDepth(image.Format); data = AstcDecoder.DecodeToRgba8888( data, textureBlockWidth, textureBlockHeight, textureBlockDepth, image.Width, image.Height, image.Depth); image.Format = GalImageFormat.Rgba8 | (image.Format & GalImageFormat.TypeMask); } (PixelInternalFormat internalFmt, PixelFormat format, PixelType type) = OglEnumConverter.GetImageFormat(image.Format); switch (target) { case TextureTarget.Texture1D: GL.TexImage1D( target, level, internalFmt, image.Width, border, format, type, data); break; case TextureTarget.Texture2D: GL.TexImage2D( target, level, internalFmt, image.Width, image.Height, border, format, type, data); break; case TextureTarget.Texture3D: GL.TexImage3D( target, level, internalFmt, image.Width, image.Height, image.Depth, border, format, type, data); break; // Cube map arrays are just 2D texture arrays with 6 entries // per cube map so we can handle them in the same way case TextureTarget.TextureCubeMapArray: case TextureTarget.Texture2DArray: GL.TexImage3D( target, level, internalFmt, image.Width, image.Height, image.LayerCount, border, format, type, data); break; case TextureTarget.TextureCubeMap: Span <byte> array = new Span <byte>(data); int faceSize = ImageUtils.GetSize(image) / 6; for (int face = 0; face < 6; face++) { GL.TexImage2D( TextureTarget.TextureCubeMapPositiveX + face, level, internalFmt, image.Width, image.Height, border, format, type, array.Slice(face * faceSize, faceSize).ToArray()); } break; default: throw new NotImplementedException($"Unsupported texture target type: {target}"); } } }
public void Bind(GalPipelineState New) { BindConstBuffers(New); BindVertexLayout(New); if (New.FramebufferSrgb != _old.FramebufferSrgb) { Enable(EnableCap.FramebufferSrgb, New.FramebufferSrgb); _renderTarget.FramebufferSrgb = New.FramebufferSrgb; } if (New.FlipX != _old.FlipX || New.FlipY != _old.FlipY || New.Instance != _old.Instance) { _shader.SetExtraData(New.FlipX, New.FlipY, New.Instance); } if (New.FrontFace != _old.FrontFace) { GL.FrontFace(OglEnumConverter.GetFrontFace(New.FrontFace)); } if (New.CullFaceEnabled != _old.CullFaceEnabled) { Enable(EnableCap.CullFace, New.CullFaceEnabled); } if (New.CullFaceEnabled) { if (New.CullFace != _old.CullFace) { GL.CullFace(OglEnumConverter.GetCullFace(New.CullFace)); } } if (New.DepthTestEnabled != _old.DepthTestEnabled) { Enable(EnableCap.DepthTest, New.DepthTestEnabled); } if (New.DepthWriteEnabled != _old.DepthWriteEnabled) { GL.DepthMask(New.DepthWriteEnabled); } if (New.DepthTestEnabled) { if (New.DepthFunc != _old.DepthFunc) { GL.DepthFunc(OglEnumConverter.GetDepthFunc(New.DepthFunc)); } } if (New.DepthRangeNear != _old.DepthRangeNear || New.DepthRangeFar != _old.DepthRangeFar) { GL.DepthRange(New.DepthRangeNear, New.DepthRangeFar); } if (New.StencilTestEnabled != _old.StencilTestEnabled) { Enable(EnableCap.StencilTest, New.StencilTestEnabled); } if (New.StencilTwoSideEnabled != _old.StencilTwoSideEnabled) { Enable((EnableCap)All.StencilTestTwoSideExt, New.StencilTwoSideEnabled); } if (New.StencilTestEnabled) { if (New.StencilBackFuncFunc != _old.StencilBackFuncFunc || New.StencilBackFuncRef != _old.StencilBackFuncRef || New.StencilBackFuncMask != _old.StencilBackFuncMask) { GL.StencilFuncSeparate( StencilFace.Back, OglEnumConverter.GetStencilFunc(New.StencilBackFuncFunc), New.StencilBackFuncRef, New.StencilBackFuncMask); } if (New.StencilBackOpFail != _old.StencilBackOpFail || New.StencilBackOpZFail != _old.StencilBackOpZFail || New.StencilBackOpZPass != _old.StencilBackOpZPass) { GL.StencilOpSeparate( StencilFace.Back, OglEnumConverter.GetStencilOp(New.StencilBackOpFail), OglEnumConverter.GetStencilOp(New.StencilBackOpZFail), OglEnumConverter.GetStencilOp(New.StencilBackOpZPass)); } if (New.StencilBackMask != _old.StencilBackMask) { GL.StencilMaskSeparate(StencilFace.Back, New.StencilBackMask); } if (New.StencilFrontFuncFunc != _old.StencilFrontFuncFunc || New.StencilFrontFuncRef != _old.StencilFrontFuncRef || New.StencilFrontFuncMask != _old.StencilFrontFuncMask) { GL.StencilFuncSeparate( StencilFace.Front, OglEnumConverter.GetStencilFunc(New.StencilFrontFuncFunc), New.StencilFrontFuncRef, New.StencilFrontFuncMask); } if (New.StencilFrontOpFail != _old.StencilFrontOpFail || New.StencilFrontOpZFail != _old.StencilFrontOpZFail || New.StencilFrontOpZPass != _old.StencilFrontOpZPass) { GL.StencilOpSeparate( StencilFace.Front, OglEnumConverter.GetStencilOp(New.StencilFrontOpFail), OglEnumConverter.GetStencilOp(New.StencilFrontOpZFail), OglEnumConverter.GetStencilOp(New.StencilFrontOpZPass)); } if (New.StencilFrontMask != _old.StencilFrontMask) { GL.StencilMaskSeparate(StencilFace.Front, New.StencilFrontMask); } } // Scissor Test // All scissor test are disabled before drawing final framebuffer to screen so we don't need to handle disabling // Skip if there are no scissor tests to enable if (New.ScissorTestCount != 0) { int scissorsApplied = 0; bool applyToAll = false; for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) { if (New.ScissorTestEnabled[index]) { // If viewport arrays are unavailable apply first scissor test to all or // there is only 1 scissor test and it's the first, the scissor test applies to all viewports if (!OglExtension.Required.ViewportArray || (index == 0 && New.ScissorTestCount == 1)) { GL.Enable(EnableCap.ScissorTest); applyToAll = true; } else { GL.Enable(IndexedEnableCap.ScissorTest, index); } if (New.ScissorTestEnabled[index] != _old.ScissorTestEnabled[index] || New.ScissorTestX[index] != _old.ScissorTestX[index] || New.ScissorTestY[index] != _old.ScissorTestY[index] || New.ScissorTestWidth[index] != _old.ScissorTestWidth[index] || New.ScissorTestHeight[index] != _old.ScissorTestHeight[index]) { if (applyToAll) { GL.Scissor(New.ScissorTestX[index], New.ScissorTestY[index], New.ScissorTestWidth[index], New.ScissorTestHeight[index]); } else { GL.ScissorIndexed(index, New.ScissorTestX[index], New.ScissorTestY[index], New.ScissorTestWidth[index], New.ScissorTestHeight[index]); } } // If all scissor tests have been applied, or viewport arrays are unavailable we can skip remaining iterations if (!OglExtension.Required.ViewportArray || ++scissorsApplied == New.ScissorTestCount) { break; } } } } if (New.BlendIndependent) { for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) { SetBlendState(index, New.Blends[index], _old.Blends[index]); } } else { if (New.BlendIndependent != _old.BlendIndependent) { SetAllBlendState(New.Blends[0]); } else { SetBlendState(New.Blends[0], _old.Blends[0]); } } if (New.ColorMaskCommon) { if (New.ColorMaskCommon != _old.ColorMaskCommon || !New.ColorMasks[0].Equals(_old.ColorMasks[0])) { GL.ColorMask( New.ColorMasks[0].Red, New.ColorMasks[0].Green, New.ColorMasks[0].Blue, New.ColorMasks[0].Alpha); } } else { for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) { if (!New.ColorMasks[index].Equals(_old.ColorMasks[index])) { GL.ColorMask( index, New.ColorMasks[index].Red, New.ColorMasks[index].Green, New.ColorMasks[index].Blue, New.ColorMasks[index].Alpha); } } } if (New.PrimitiveRestartEnabled != _old.PrimitiveRestartEnabled) { Enable(EnableCap.PrimitiveRestart, New.PrimitiveRestartEnabled); } if (New.PrimitiveRestartEnabled) { if (New.PrimitiveRestartIndex != _old.PrimitiveRestartIndex) { GL.PrimitiveRestartIndex(New.PrimitiveRestartIndex); } } _old = New; }