@ -13,11 +13,13 @@ using static Avalonia.LinuxFramebuffer.Output.LibDrm;
namespace Avalonia.LinuxFramebuffer.Output
{
public unsafe class DrmOutput : IGlOutputBackend , IGlPlatformSurface
public unsafe class DrmOutput : IGlOutputBackend , IGlPlatformSurface , ISurfaceOrientation
{
private DrmOutputOptions _ outputOptions = new ( ) ;
private DrmCard _ card ;
public PixelSize PixelSize = > _ mode . Resolution ;
public PixelSize PixelSize = > Orientation = = SurfaceOrientation . Rotation0 | | Orientation = = SurfaceOrientation . Rotation180
? new PixelSize ( _ mode . Resolution . Width , _ mode . Resolution . Height )
: new PixelSize ( _ mode . Resolution . Height , _ mode . Resolution . Width ) ;
public double Scaling
{
@ -25,6 +27,12 @@ namespace Avalonia.LinuxFramebuffer.Output
set = > _ outputOptions . Scaling = value ;
}
public SurfaceOrientation Orientation
{
get = > _ outputOptions . Orientation ;
set = > _ outputOptions . Orientation = value ;
}
class SharedContextGraphics : IPlatformGraphics
{
private readonly IPlatformGraphicsContext _ context ;
@ -113,6 +121,12 @@ namespace Avalonia.LinuxFramebuffer.Output
private IntPtr _ currentBo ;
private IntPtr _ gbmTargetSurface ;
private uint _ crtcId ;
private int _ rotationFbo ;
private int _ rotationTexture ;
private PixelSize _ rotatedSize ;
private int _ rotationProgram ;
private int _ rotationVbo ;
private int _ rotationVao ;
void FbDestroyCallback ( IntPtr bo , IntPtr userData )
{
@ -157,7 +171,6 @@ namespace Avalonia.LinuxFramebuffer.Output
return fbHandle ;
}
[MemberNotNull(nameof(_card))]
[MemberNotNull(nameof(PlatformGraphics))]
[MemberNotNull(nameof(FbDestroyDelegate))]
@ -236,6 +249,129 @@ namespace Avalonia.LinuxFramebuffer.Output
_ mode = mode ;
_ currentBo = bo ;
// Initialize FBO for rotation if needed
var needsRotation = _ outputOptions . Orientation ! = SurfaceOrientation . Rotation0 ;
if ( needsRotation )
{
// For 90/270 rotation, swap width and height
_ rotatedSize = ( _ outputOptions . Orientation = = SurfaceOrientation . Rotation90 | |
_ outputOptions . Orientation = = SurfaceOrientation . Rotation270 )
? new PixelSize ( modeInfo . Resolution . Height , modeInfo . Resolution . Width )
: modeInfo . Resolution ;
using ( _d eferredContext . MakeCurrent ( _ eglSurface ) )
{
var gl = _d eferredContext . GlInterface ;
_ rotationFbo = gl . GenFramebuffer ( ) ;
_ rotationTexture = gl . GenTexture ( ) ;
gl . BindTexture ( GlConsts . GL_TEXTURE_2D , _ rotationTexture ) ;
gl . TexImage2D ( GlConsts . GL_TEXTURE_2D , 0 , GlConsts . GL_RGBA , _ rotatedSize . Width , _ rotatedSize . Height , 0 ,
GlConsts . GL_RGBA , GlConsts . GL_UNSIGNED_BYTE , IntPtr . Zero ) ;
gl . TexParameteri ( GlConsts . GL_TEXTURE_2D , GlConsts . GL_TEXTURE_MIN_FILTER , GlConsts . GL_LINEAR ) ;
gl . TexParameteri ( GlConsts . GL_TEXTURE_2D , GlConsts . GL_TEXTURE_MAG_FILTER , GlConsts . GL_LINEAR ) ;
gl . BindFramebuffer ( GlConsts . GL_FRAMEBUFFER , _ rotationFbo ) ;
gl . FramebufferTexture2D ( GlConsts . GL_FRAMEBUFFER , GlConsts . GL_COLOR_ATTACHMENT0 ,
GlConsts . GL_TEXTURE_2D , _ rotationTexture , 0 ) ;
gl . BindFramebuffer ( GlConsts . GL_FRAMEBUFFER , 0 ) ;
// Create shader program for textured quad
const string vertexShader = @ "
attribute vec2 aPos ;
attribute vec2 aTexCoord ;
varying vec2 vTexCoord ;
void main ( ) {
gl_Position = vec4 ( aPos , 0.0 , 1.0 ) ;
vTexCoord = aTexCoord ;
} ";
const string fragmentShader = @ "
precision mediump float ;
varying vec2 vTexCoord ;
uniform sampler2D uTexture ;
void main ( ) {
gl_FragColor = texture2D ( uTexture , vTexCoord ) ;
} ";
var vs = gl . CreateShader ( GlConsts . GL_VERTEX_SHADER ) ;
gl . ShaderSourceString ( vs , vertexShader ) ;
gl . CompileShader ( vs ) ;
var fs = gl . CreateShader ( GlConsts . GL_FRAGMENT_SHADER ) ;
gl . ShaderSourceString ( fs , fragmentShader ) ;
gl . CompileShader ( fs ) ;
_ rotationProgram = gl . CreateProgram ( ) ;
gl . AttachShader ( _ rotationProgram , vs ) ;
gl . AttachShader ( _ rotationProgram , fs ) ;
gl . LinkProgram ( _ rotationProgram ) ;
gl . DeleteShader ( vs ) ;
gl . DeleteShader ( fs ) ;
// Create VBO with quad vertices - texture coords depend on rotation
// Format: x, y, u, v
float [ ] vertices = _ outputOptions . Orientation switch
{
SurfaceOrientation . Rotation90 = > new float [ ] {
// 90° clockwise rotation
- 1.0f , - 1.0f , 1.0f , 0.0f , // Bottom-left -> Bottom-right of texture
1.0f , - 1.0f , 1.0f , 1.0f , // Bottom-right -> Top-right of texture
1.0f , 1.0f , 0.0f , 1.0f , // Top-right -> Top-left of texture
- 1.0f , 1.0f , 0.0f , 0.0f // Top-left -> Bottom-left of texture
} ,
SurfaceOrientation . Rotation180 = > new float [ ] {
// 180° rotation
- 1.0f , - 1.0f , 1.0f , 1.0f , // Bottom-left -> Top-right of texture
1.0f , - 1.0f , 0.0f , 1.0f , // Bottom-right -> Top-left of texture
1.0f , 1.0f , 0.0f , 0.0f , // Top-right -> Bottom-left of texture
- 1.0f , 1.0f , 1.0f , 0.0f // Top-left -> Bottom-right of texture
} ,
SurfaceOrientation . Rotation270 = > new float [ ] {
// 270° clockwise (90° counter-clockwise) rotation
- 1.0f , - 1.0f , 0.0f , 1.0f , // Bottom-left -> Top-left of texture
1.0f , - 1.0f , 0.0f , 0.0f , // Bottom-right -> Bottom-left of texture
1.0f , 1.0f , 1.0f , 0.0f , // Top-right -> Bottom-right of texture
- 1.0f , 1.0f , 1.0f , 1.0f // Top-left -> Top-right of texture
} ,
_ = > new float [ ] {
// No rotation (shouldn't reach here but fallback)
- 1.0f , - 1.0f , 0.0f , 0.0f ,
1.0f , - 1.0f , 1.0f , 0.0f ,
1.0f , 1.0f , 1.0f , 1.0f ,
- 1.0f , 1.0f , 0.0f , 1.0f
}
} ;
_ rotationVbo = gl . GenBuffer ( ) ;
_ rotationVao = gl . GenVertexArray ( ) ;
gl . BindVertexArray ( _ rotationVao ) ;
gl . BindBuffer ( GlConsts . GL_ARRAY_BUFFER , _ rotationVbo ) ;
fixed ( float * ptr = vertices )
{
gl . BufferData ( GlConsts . GL_ARRAY_BUFFER , new IntPtr ( vertices . Length * sizeof ( float ) ) ,
new IntPtr ( ptr ) , GlConsts . GL_STATIC_DRAW ) ;
}
var posAttrib = gl . GetAttribLocationString ( _ rotationProgram , "aPos" ) ;
gl . EnableVertexAttribArray ( posAttrib ) ;
gl . VertexAttribPointer ( posAttrib , 2 , GlConsts . GL_FLOAT , 0 , 4 * sizeof ( float ) , IntPtr . Zero ) ;
var texAttrib = gl . GetAttribLocationString ( _ rotationProgram , "aTexCoord" ) ;
gl . EnableVertexAttribArray ( texAttrib ) ;
gl . VertexAttribPointer ( texAttrib , 2 , GlConsts . GL_FLOAT , 0 , 4 * sizeof ( float ) , new IntPtr ( 2 * sizeof ( float ) ) ) ;
gl . BindVertexArray ( 0 ) ;
}
}
else
{
// No rotation needed
_ rotatedSize = modeInfo . Resolution ;
}
if ( _ outputOptions . EnableInitialBufferSwapping )
{
@ -288,7 +424,39 @@ namespace Avalonia.LinuxFramebuffer.Output
public void Dispose ( )
{
_ parent . _d eferredContext . GlInterface . Flush ( ) ;
var gl = _ parent . _d eferredContext . GlInterface ;
if ( _ parent . _ outputOptions . Orientation ! = SurfaceOrientation . Rotation0 )
{
// Rotation enabled - blit from FBO to screen
// Unbind FBO to render to default framebuffer
gl . BindFramebuffer ( GlConsts . GL_FRAMEBUFFER , 0 ) ;
gl . Viewport ( 0 , 0 , _ parent . _ mode . Resolution . Width , _ parent . _ mode . Resolution . Height ) ;
// Clear the screen
gl . ClearColor ( 0 , 0 , 0 , 1 ) ;
gl . Clear ( GlConsts . GL_COLOR_BUFFER_BIT ) ;
// Use the shader program
gl . UseProgram ( _ parent . _ rotationProgram ) ;
// Bind the FBO texture
gl . ActiveTexture ( GlConsts . GL_TEXTURE0 ) ;
gl . BindTexture ( GlConsts . GL_TEXTURE_2D , _ parent . _ rotationTexture ) ;
// Set texture uniform (texture unit 0)
var texLoc = gl . GetUniformLocationString ( _ parent . _ rotationProgram , "uTexture" ) ;
gl . Uniform1i ( texLoc , 0 ) ;
// Draw the rotated quad
gl . BindVertexArray ( _ parent . _ rotationVao ) ;
gl . DrawArrays ( GlConsts . GL_TRIANGLE_FAN , 0 , 4 ) ;
gl . BindVertexArray ( 0 ) ;
gl . UseProgram ( 0 ) ;
}
gl . Flush ( ) ;
_ parent . _ eglSurface . SwapBuffers ( ) ;
var nextBo = gbm_surface_lock_front_buffer ( _ parent . _ gbmTargetSurface ) ;
@ -333,7 +501,7 @@ namespace Avalonia.LinuxFramebuffer.Output
public IGlContext Context = > _ parent . _d eferredContext ;
public PixelSize Size = > _ parent . _ mode . Resolution ;
public PixelSize Size = > _ parent . _ rotatedSize ;
public double Scaling = > _ parent . Scaling ;
@ -342,7 +510,23 @@ namespace Avalonia.LinuxFramebuffer.Output
public IGlPlatformSurfaceRenderingSession BeginDraw ( )
{
return new RenderSession ( _ parent , _ parent . _d eferredContext . MakeCurrent ( _ parent . _ eglSurface ) ) ;
var clearContext = _ parent . _d eferredContext . MakeCurrent ( _ parent . _ eglSurface ) ;
var gl = _ parent . _d eferredContext . GlInterface ;
if ( _ parent . _ outputOptions . Orientation ! = SurfaceOrientation . Rotation0 )
{
// Bind FBO for rendering when rotation is enabled
gl . BindFramebuffer ( GlConsts . GL_FRAMEBUFFER , _ parent . _ rotationFbo ) ;
gl . Viewport ( 0 , 0 , _ parent . _ rotatedSize . Width , _ parent . _ rotatedSize . Height ) ;
}
else
{
// Render directly to screen when no rotation
gl . BindFramebuffer ( GlConsts . GL_FRAMEBUFFER , 0 ) ;
gl . Viewport ( 0 , 0 , _ parent . _ mode . Resolution . Width , _ parent . _ mode . Resolution . Height ) ;
}
return new RenderSession ( _ parent , clearContext ) ;
}
}