Skip to content

Commit

Permalink
Add initial support for compute shaders.
Browse files Browse the repository at this point in the history
  • Loading branch information
Roldak committed Mar 7, 2022
1 parent bc4f75e commit e8e4dd1
Show file tree
Hide file tree
Showing 12 changed files with 740 additions and 389 deletions.
516 changes: 263 additions & 253 deletions opengl/src/generated/gl-api.ads

Large diffs are not rendered by default.

273 changes: 140 additions & 133 deletions opengl/src/generated/gl-load_function_pointers.adb

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions opengl/src/implementation/gl-compute.adb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
-- part of OpenGLAda, (c) 2022 Felix Krause
-- released under the terms of the MIT license, see the file "COPYING"

with GL.Enums.Getter;
with GL.API;

package body GL.Compute is
function Max_Compute_Work_Group_Count (Index : Index_Type) return Int is
Value : aliased Int;
begin
API.Get_Integer_Indexed
(GL.Enums.Getter.Max_Compute_Work_Group_Count,
Index_Type'Pos (Index),
Value'Access);
Raise_Exception_On_OpenGL_Error;
return Value;
end Max_Compute_Work_Group_Count;

function Max_Compute_Work_Group_Size (Index : Index_Type) return Int is
Value : aliased Int;
begin
API.Get_Integer_Indexed
(GL.Enums.Getter.Max_Compute_Work_Group_Size,
Index_Type'Pos (Index),
Value'Access);
Raise_Exception_On_OpenGL_Error;
return Value;
end Max_Compute_Work_Group_Size;

function Max_Compute_Work_Group_Invocations return Int is
Value : aliased Int;
begin
API.Get_Integer
(GL.Enums.Getter.Max_Compute_Work_Group_Invocations,
Value'Access);
Raise_Exception_On_OpenGL_Error;
return Value;
end Max_Compute_Work_Group_Invocations;

procedure Dispatch_Compute
(Num_Groups_X, Num_Groups_Y, Num_Groups_Z : UInt) is
begin
API.Dispatch_Compute (Num_Groups_X, Num_Groups_Y, Num_Groups_Z);
Raise_Exception_On_OpenGL_Error;
end Dispatch_Compute;
end GL.Compute;


6 changes: 6 additions & 0 deletions opengl/src/implementation/gl-enums-getter.ads
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,12 @@ package GL.Enums.Getter is
Stencil_Back_Ref,
Stencil_Back_Value_Mask,
Stencil_Back_Writemask,
Max_Compute_Work_Group_Invocations,
Max_Debug_Message_Length,
Max_Debug_Logged_Messages,
Debug_Logged_Messages,
Max_Compute_Work_Group_Count,
Max_Compute_Work_Group_Size,
Max_Framebuffer_Width,
Max_Framebuffer_Height,
Max_Framebuffer_Layers,
Expand Down Expand Up @@ -497,9 +500,12 @@ package GL.Enums.Getter is
Stencil_Back_Ref => 16#8CA3#,
Stencil_Back_Value_Mask => 16#8CA4#,
Stencil_Back_Writemask => 16#8CA5#,
Max_Compute_Work_Group_Invocations => 16#90EB#,
Max_Debug_Message_Length => 16#9143#,
Max_Debug_Logged_Messages => 16#9144#,
Debug_Logged_Messages => 16#9145#,
Max_Compute_Work_Group_Count => 16#91BE#,
Max_Compute_Work_Group_Size => 16#91BF#,
Max_Framebuffer_Width => 16#9315#,
Max_Framebuffer_Height => 16#9316#,
Max_Framebuffer_Layers => 16#9317#,
Expand Down
24 changes: 24 additions & 0 deletions opengl/src/implementation/gl-memory.adb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
-- part of OpenGLAda, (c) 2022 Felix Krause
-- released under the terms of the MIT license, see the file "COPYING"

with Ada.Unchecked_Conversion;

with GL.API;

package body GL.Memory is
function To_BitField is new Ada.Unchecked_Conversion
(Barrier_Bits, GL.Low_Level.Bitfield);

procedure Barrier (Bits : Barrier_Bits) is
begin
API.Memory_Barrier (To_BitField (Bits));
Raise_Exception_On_OpenGL_Error;
end Barrier;

procedure Barrier_By_Region (Bits : Barrier_Bits) is
begin
API.Memory_Barrier_By_Region (To_BitField (Bits));
Raise_Exception_On_OpenGL_Error;
end Barrier_By_Region;
end GL.Memory;

23 changes: 23 additions & 0 deletions opengl/src/interface/gl-compute.ads
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-- part of OpenGLAda, (c) 2022 Felix Krause
-- released under the terms of the MIT license, see the file "COPYING"

with GL.Types;

package GL.Compute is
pragma Preelaborate;

use GL.Types;

type Index_Type is (X, Y, Z);

function Max_Compute_Work_Group_Count (Index : Index_Type) return Int;
function Max_Compute_Work_Group_Size (Index : Index_Type) return Int;
function Max_Compute_Work_Group_Invocations return Int;

procedure Dispatch_Compute
(Num_Groups_X, Num_Groups_Y, Num_Groups_Z : UInt);

private
for Index_Type use (X => 0, Y => 1, Z => 2);
end GL.Compute;

58 changes: 58 additions & 0 deletions opengl/src/interface/gl-memory.ads
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
-- part of OpenGLAda, (c) 2022 Felix Krause
-- released under the terms of the MIT license, see the file "COPYING"

private with GL.Low_Level;

package GL.Memory is
pragma Preelaborate;

type Barrier_Bits is record
Vertex_Attrib_Array : Boolean := False;
Element_Array : Boolean := False;
Uniform : Boolean := False;
Texture_Fetch : Boolean := False;

Shader_Image_Access : Boolean := False;
Command : Boolean := False;
Pixel_Buffer : Boolean := False;
Texture_Update : Boolean := False;
Buffer_Update : Boolean := False;
Framebuffer : Boolean := False;
Transform_Feedback : Boolean := False;
Atomic_Counter : Boolean := False;
Shader_Storage : Boolean := False;
Client_Mapped_Buffer : Boolean := False;
Query_Buffer : Boolean := False;

Unused : Boolean := False;
end record;
pragma Convention (C, Barrier_Bits);

procedure Barrier (Bits : Barrier_Bits);

procedure Barrier_By_Region (Bits : Barrier_Bits);

private
for Barrier_Bits use record
Vertex_Attrib_Array at 0 range 0 .. 0;
Element_Array at 0 range 1 .. 1;
Uniform at 0 range 2 .. 2;
Texture_Fetch at 0 range 3 .. 3;

Shader_Image_Access at 0 range 5 .. 5;
Command at 0 range 6 .. 6;
Pixel_Buffer at 0 range 7 .. 7;
Texture_Update at 0 range 8 .. 8;
Buffer_Update at 0 range 9 .. 9;
Framebuffer at 0 range 10 .. 10;
Transform_Feedback at 0 range 11 .. 11;
Atomic_Counter at 0 range 12 .. 12;
Shader_Storage at 0 range 13 .. 13;
Client_Mapped_Buffer at 0 range 14 .. 14;
Query_Buffer at 0 range 15 .. 15;

Unused at 0 range 16 .. 31;
end record;
for Barrier_Bits'Size use Low_Level.Bitfield'Size;
end GL.Memory;

6 changes: 4 additions & 2 deletions opengl/src/interface/gl-objects-shaders.ads
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ package GL.Objects.Shaders is
pragma Preelaborate;

type Shader_Type is (Fragment_Shader, Vertex_Shader, Geometry_Shader,
Tess_Evaluation_Shader, Tess_Control_Shader);
Tess_Evaluation_Shader, Tess_Control_Shader,
Compute_Shader);

type Shader (Kind : Shader_Type) is new GL_Object with private;

Expand Down Expand Up @@ -39,7 +40,8 @@ private
Vertex_Shader => 16#8B31#,
Geometry_Shader => 16#8DD9#,
Tess_Evaluation_Shader => 16#8E87#,
Tess_Control_Shader => 16#8E88#);
Tess_Control_Shader => 16#8E88#,
Compute_Shader => 16#91B9#);
for Shader_Type'Size use Low_Level.Enum'Size;

end GL.Objects.Shaders;
24 changes: 24 additions & 0 deletions opengl/src/specs/gl-api.spec
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ spec GL.API is
Static => "glGetFloatv";
procedure Get_Integer (Name : Enums.Getter.Parameter;
Target : access Int) with Static => "glGetIntegerv";
procedure Get_Integer_Indexed (Name : Enums.Getter.Parameter;
Index : UInt;
Target : access Int) with
Dynamic => "glGetIntegeri_v";
procedure Get_Int_Vec4 (Name : Enums.Getter.Parameter;
Target : in out Ints.Vector4) with
Static => "glGetIntegerv";
Expand Down Expand Up @@ -1300,4 +1304,24 @@ spec GL.API is
Static => "glDepthRange", Wrapper => "GL.Window.Set_Depth_Range";
procedure Viewport (X, Y : Int; Width, Height : Size) with
Static => "glViewport", Wrapper => "GL.Window.Set_Viewport";

-----------------------------------------------------------------------------
-- Compute Shaders --
-----------------------------------------------------------------------------

procedure Dispatch_Compute
(Num_Groups_X, Num_Groups_Y, Num_Groups_Z : UInt) with
Dynamic => "glDispatchCompute",
Wrapper => "GL.Compute.Dispatch_Compute";

-----------------------------------------------------------------------------
-- Memory Barriers --
-----------------------------------------------------------------------------

procedure Memory_Barrier (Bits : Low_Level.Bitfield) with
Dynamic => "glMemoryBarrier",
Wrapper => "GL.Memory.Barrier";
procedure Memory_Barrier_By_Region (Bits : Low_Level.Bitfield) with
Dynamic => "glMemoryBarrierByRegion",
Wrapper => "GL.Memory.Barrier_By_Region";
end GL.API;
2 changes: 1 addition & 1 deletion tests/opengl-test.gpr
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ project OpenGL.Test is

for Main use ("gl_test-vbos", "gl_test-immediate", "gl_test-shaders",
"gl_test-opengl3", "gl_test-context", "gl_test-framebuffers",
"gl_test-debugging");
"gl_test-debugging", "gl_test-compute");

package Ide renames OpenGL.Ide;
package Builder renames OpenGL.Builder;
Expand Down
10 changes: 10 additions & 0 deletions tests/src/gl/gl_test-compute-shader.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#version 430
layout(local_size_x = 32) in;
layout(std430, binding = 0) buffer data {
float values[];
};

void main() {
int x = int(gl_GlobalInvocationID.x);
values[x] = 2 * values[x];
}
139 changes: 139 additions & 0 deletions tests/src/gl/gl_test-compute.adb
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
-- part of OpenGLAda, (c) 2022 Felix Krause
-- released under the terms of the MIT license, see the file "COPYING"

with Ada.Text_IO;

with GL.Compute;
with GL.Files;
with GL.Memory;
with GL.Objects.Buffers;
with GL.Objects.Shaders;
with GL.Objects.Programs;
with GL.Types;

with GL_Test.Display_Backend;

procedure GL_Test.Compute is
use GL.Types;

procedure Load_Singles is new GL.Objects.Buffers.Load_To_Buffer
(Single_Pointers);

procedure Map_Singles is new GL.Objects.Buffers.Map
(Single_Pointers);

Data_Count : constant := 1024;

Iteration_Count : Natural := 0;

Buffer : GL.Objects.Buffers.Buffer;
Invalid : GL.Objects.Buffers.Buffer;
Compute_Shader : GL.Objects.Shaders.Shader
(Kind => GL.Objects.Shaders.Compute_Shader);
Program : GL.Objects.Programs.Program;
begin
Display_Backend.Init;
Display_Backend.Open_Window (Width => 500, Height => 500);

Buffer.Initialize_Id;
Invalid.Initialize_Id;
Compute_Shader.Initialize_Id;
Program.Initialize_Id;

-- initialize an SSBO with numbers from 1 to `Data_Count`
declare
use GL.Objects.Buffers;

Values : Single_Array (1 .. Data_Count);
begin
for I In Values'Range loop
Values (I) := Single (I);
end loop;
Shader_Storage_Buffer.Bind (Buffer);
Load_Singles
(Shader_Storage_Buffer,
Values,
Dynamic_Draw);
-- The following dummy bind is necessary for the Bind_Buffer_Base
-- to be effective. TODO: determine if it's a bug or not.
Shader_Storage_Buffer.Bind (Invalid);
Shader_Storage_Buffer.Bind_Buffer_Base (0, Buffer);
end;

-- load shader sources and compile shaders
GL.Files.Load_Shader_Source_From_File
(Compute_Shader, "../src/gl/gl_test-compute-shader.glsl");

Compute_Shader.Compile;

if not Compute_Shader.Compile_Status then
Ada.Text_IO.Put_Line ("Compilation of compute shader failed. log:");
Ada.Text_IO.Put_Line (Compute_Shader.Info_Log);
end if;

-- set up program
Program.Attach (Compute_Shader);
Program.Link;
if not Program.Link_Status then
Ada.Text_IO.Put_Line ("Program linking failed. Log:");
Ada.Text_IO.Put_Line (Program.Info_Log);
return;
end if;

-- check various constant
declare
use GL.Compute;
begin
for Index in Index_Type loop
Ada.Text_IO.Put ("Max compute work group count " & Index'Image & " :");
Ada.Text_IO.Put_Line (Int'Image (Max_Compute_Work_Group_Count (Index)));
end loop;

for Index in Index_Type loop
Ada.Text_IO.Put ("Max compute work group size " & Index'Image & " :");
Ada.Text_IO.Put_Line (Int'Image (Max_Compute_Work_Group_Size (Index)));
end loop;

Ada.Text_IO.Put ("Max compute work group invocations :");
Ada.Text_IO.Put_Line (Int'Image (Max_Compute_Work_Group_Invocations));
end;

Program.Use_Program;

-- dispatch compute shader 5 times
while Iteration_Count < 5 loop
-- local size for x axis is 32 as specified in the shader
GL.Compute.Dispatch_Compute (Data_Count / 32, 1, 1);
GL.Memory.Barrier ((Shader_Storage => True, others => False));
Iteration_Count := Iteration_Count + 1;
end loop;

-- print the final result to compare to the expected value
declare
use GL.Objects;
use GL.Objects.Buffers;

CPU_Total : Single := 0.0;
GPU_Total : Single := 0.0;

Value_Ptr : Single_Pointers.Pointer;
begin
Map_Singles (Shader_Storage_Buffer, Read_Only, Value_Ptr);

for I in 1 .. Data_Count loop
-- We run 5 iterations of the compute shader which simply doubles its
-- previous value, so this is ultimately the same as multiplying the
-- initial value by 2 ** 5 = 32.
CPU_Total := CPU_Total + Single (I) * 32.0;
GPU_Total := GPU_Total + Value_Ptr.all;
Single_Pointers.Increment (Value_Ptr);
end loop;

Unmap (Shader_Storage_Buffer);

Ada.Text_IO.Put_Line ("CPU Total : " & CPU_Total'Image);
Ada.Text_IO.Put_Line ("GPU Total : " & GPU_Total'Image);
end;

Display_Backend.Shutdown;
end GL_Test.Compute;

0 comments on commit e8e4dd1

Please sign in to comment.