Contexts¶
To render 3D graphics with OpenGL in a window or to perform general-purpose computing on the GPU, a context needs to be created first. A context must be current on exactly one task and only rendering commands from that task are considered valid commands for that particular context.
In Orka, a context is automatically made current on the calling task after it is created, but the context can be moved to another task by first making it non-current in the first task and then making it current in the other task.
Depending on the platform, a context can be attached to a window when it is made current. After attaching the context to a window, a default framebuffer will be available for drawing commands. Any drawing commands that draw to the default framebuffer will render to the surface of the window to which the context has been attached.
Creating a context¶
An OpenGL context can be created with either AWT or EGL. Using AWT requires the presence of a windowing system such as Wayland. EGL supports multiple platforms and can be used on Wayland as well as without any windowing system by directly using a GPU device.
A context is created by calling the function Create_Context
in the packages
Orka.Contexts.AWT
or Orka.Contexts.EGL
.
The requested version of OpenGL must be given and optionally some flags.
The following flags can be used:
- Debug. Creates a debug context. The video driver may generate errors,
warnings, and other useful information. If
Debug
isFalse
then the video driver may choose to not enable full debug output. In order to actually enable log messages, make sure to callOrka.Debug.Set_Log_Messages
. See Logging for more information. - Robust. Creates a context where out-of-bound writes to buffers are discarded and reads return zero. In addition, the GL context can be queried for graphics resets that have occurred.
- No_Error. Creates a context where any OpenGL API call will not generate an error, but instead cause undefined behavior. This might increase performance a bit, but should only be enabled after having verified that errors do not occur.
AWT is currently not available on Windows
Currently, AWT has a backend for Wayland only. If you are on Windows, there are several options:
-
Use WSLg with the d3d12 Gallium driver from Mesa. See Mesamatrix for a list of extensions supported by the d3d12 driver.
-
Write a backend for AWT using the win32ada Alire crate for Windows. If and once it has been made open source under the Apache-2.0 license, you could use code from aglw.
-
Create a context and window using SDL through the sdlada crate.
-
Use a surfaceless context with EGL, assuming you can install and link with the EGL library from the
mingw-w64-x86_64-mesa
package on msys2. The mentioned package should be installed automatically by Alire as a dependency of the Alire crate orka_egl.
AWT¶
When using AWT, an OpenGL context must be created first:
Context : constant Orka.Contexts.Surface_Context'Class := Orka.Contexts.AWT.Create_Context
(Version => (4, 2),
Flags => (Debug | Robust => True, others => False));
Window : aliased Orka.Windows.Window'Class := Orka.Contexts.AWT.Create_Window
(Context, Width => 1280, Height => 720);
The context is created and made current on the calling task by the Create_Context
function.
Tip
Call function Framebuffer_Resized
in the rendering task to detect
that the default framebuffer must be recreated before rendering to it.
EGL¶
If a windowing system is not available or depending on it is not desired, EGL can be used directly instead:
Context : constant Orka.Contexts.Context'Class := Orka.Contexts.EGL.Create_Context
(Version => (4, 2),
Flags => (Debug => True, others => False));
This will create a context using the 'device' platform of EGL.
The context is created and made current on the calling task by the Create_Context
function.
Note
A context that uses the Wayland platform can be created using the function
Create_Context
in package Orka.Contexts.EGL.Wayland
.
The Wayland platform is also used when creating a context on Linux via AWT.
Listing supported EGL extensions
The Alire crate orka_egl provides the executable orka_egl_info, which will print the supported client and platforms extensions.
Enumerating devices¶
The function Devices
in the package EGL.Objects.Devices
can return
a list of devices:
Devices : constant EGL.Objects.Devices.Device_List := EGL.Objects.Devices.Devices;
Device_1 : EGL.Objects.Devices.Device renames Devices (Devices'First);
On Linux, this function will usually return two devices: a device that represents the video card in the machine and a 'device' that does software rendering on the CPU.
The name of a device can be retrieved via the function Name
of the device.
If the name cannot be retrieved, an empty string is returned. A list of device
extensions that a device supports can be obtained via the function Extensions
:
for Extension of Device_1.Extensions loop
Ada.Text_IO.Put_Line (EGL.SU.To_String (Extension));
end loop;
If the device has an extension called EGL_MESA_device_software
then rendering
will happen on the CPU.
Creating a context for a device¶
Package Orka.Contexts.EGL
has a second function Create_Context
that
uses a device as the first parameter to create a context using that particular
device:
Context : constant Orka.Contexts.Context'Class := Orka.Contexts.EGL.Create_Context
(Device => Device_1,
Version => (4, 2),
Flags => (Debug => True, others => False));
If the parameter Device
is not given, then the first device in the list
that would be returned by function Devices
is used.
Summary
An EGL context can be created using different platforms:
-
The Wayland platform, created using
Orka.Contexts.EGL.Wayland
, allows you to render things on the screen in a window, but rendering is done using the same GPU as the one used by the Wayland compositor. -
The 'device' platform, created using
Orka.Contexts.EGL
, allows you to render off-screen using any device returned byEGL.Objects.Devices.Devices
, but the results cannot be displayed in a window.
Moving a context¶
A context is automatically made current on the calling task after it is created, but it can be moved to another task if necessary. To do so, the context must first be made non-current in the first task and the first task must then subsequently signal to the second task that it can make the context current by performing a rendezvous:
Context.Move_To (Render_Task, Window);
The second task must implement the task interface Task_With_Surface_Context
:
task Render_Task is new Orka.Contexts.Task_With_Surface_Context with
entry Move_Context
(Context : not null Orka.Contexts.Surface_Context_Access;
Window : in out Orka.Windows.Window'Class);
end Render_Task;
This task can then make the context current after accepting the Move_Context
call:
accept Move_Context
(Context : not null Orka.Contexts.Surface_Context_Access;
Window : in out Orka.Windows.Window'Class)
do
Context.Make_Current (Window);
end Move_Context;
It is recommended that the task on which the context is current calls
Context.Make_Not_Current
after completing its execution or when
some exception is raised. To do this, the task can declare a variable
containing a pointer to a context:
Context : Orka.Contexts.Surface_Context_Access;
And then set it inside the accept
block:
Render_Task.Context := Context;
Moving a surfaceless context
A surfaceless context implementing the interface Context
can be
moved to a task implementing the task interface Task_With_Context
.
It must implement the following entry:
entry Move_Context (Context : not null Orka.Contexts.Context_Access);
Spawning a render task¶
To spawn a new task for rendering and move the context to this task,
create an instance of the task types Render_To_Window_Task
(for contexts
with a surface) or Render_Task
(for contexts without a surface),
which can be found in the package Orka.Contexts.Tasks
:
declare
procedure Do_Render is
begin
-- Render to window here
end Do_Render;
Render_Task : Orka.Contexts.Tasks.Render_To_Window_Task (Do_Render'Access);
begin
Context.Move_To (Render_Task, Window);
-- Handle keyboard and pointer events here while window is not closed
end;