OpenGL events in Love for RenderDoc

Showcase your libraries, tools and other projects that help your fellow love users.
Post Reply
User avatar
Froyok
Prole
Posts: 29
Joined: Tue Nov 16, 2021 4:53 pm
Contact:

OpenGL events in Love for RenderDoc

Post by Froyok »

[EDIT] (See latest messages for an updated code snippet. Keeping the original post as-is for posterity.)

While working on my projects I wanted to use a graphic debugger like RenderDoc to check a few things.
Löve doesn't really create any events by default, so checking the OpenGL api calls in a flat list can be a bit tedious sometimes.

So here is a code snippet that shows how to manually push events that are visible in RenderDoc:

ogl.lua

Code: Select all

local FFI = require( 'ffi' )
local OGL_LIBS = {
	OSX		= { x86 = "OpenGL.framework/OpenGL", x64 = "OpenGL.framework/OpenGL" },
	Windows	= { x86 = "OPENGL32.DLL", x64 = "OPENGL32.DLL" },
	Linux	= { x86 = "libGL.so", x64 = "libGL.so", arm = "libGL.so" },
}
local OGL_LIB = OGL_LIBS[ FFI.os ][ FFI.arch ]

FFI.cdef[[
	typedef char GLchar;
	typedef int GLsizei;
	typedef unsigned int GLuint;
	typedef unsigned int GLenum;

	void glPushDebugGroup( GLenum source, GLuint id, GLsizei length, const GLchar *message );
	void glPopDebugGroup( void );
]]

local OPENGL = {
	GL = FFI.load( OGL_LIB ),

	PushEvent = function( self, Message )
		self.GL.glPushDebugGroup(
			0,
			0,
			string.len( Message ),
			Message
		)
	end,
	
	PopEvent = function( self )
		self.GL.glPopDebugGroup()
	end
}

return OPENGL
main.lua

Code: Select all

local GL = require( "ogl.lua" )

function love.load()
end

function love.update( dt )
end

function love.draw()
	GL:PushEvent( "ParentEvent" )
	
	love.graphics.rectangle( "fill", 10, 50, 60, 120 )
	
	GL:PushEvent( "ChildEvent" )

	love.graphics.rectangle( "fill", 40, 50, 60, 120 )

	GL:PopEvent()
	GL:PopEvent()
end
An exmaple with my game, before:
event_before.png
event_before.png (37.34 KiB) Viewed 8707 times
And after:
event_after.png
event_after.png (38.66 KiB) Viewed 8707 times
---

Bonus, if you want to check/use other OpenGL functions they can be found here: https://www.khronos.org/registry/OpenGL ... lcorearb.h
Last edited by Froyok on Thu Jan 12, 2023 1:13 am, edited 1 time in total.
User avatar
ReFreezed
Party member
Posts: 612
Joined: Sun Oct 25, 2015 11:32 pm
Location: Sweden
Contact:

Re: OpenGL events in Love for RenderDoc

Post by ReFreezed »

This is very helpful. Thanks!
Tools: Hot Particles, LuaPreprocess, InputField, (more) Games: Momento Temporis
"If each mistake being made is a new one, then progress is being made."
User avatar
idbrii
Prole
Posts: 34
Joined: Sat Jun 12, 2021 5:07 am

Re: OpenGL events in Love for RenderDoc

Post by idbrii »

That's really cool!

You could even add a wrapper func to make adding markers around functions easier:

Code: Select all

Call = function( self, name, fun, ... )
	    self:PushEvent( name )
	    fn(...)
	    self:PopEvent()
	end


function love.draw()
	GL:Call( "gameplay", mygame.draw, mygame ) -- calls mygame:draw()
	GL:Call( "ui", draw_ui ) -- calls draw_ui()
end
Edit: s/GL/self/c as zorg pointed out I wasn't using self properly.
Last edited by idbrii on Fri Jan 07, 2022 6:03 am, edited 1 time in total.
User avatar
Froyok
Prole
Posts: 29
Joined: Tue Nov 16, 2021 4:53 pm
Contact:

Re: OpenGL events in Love for RenderDoc

Post by Froyok »

Ha, that's interesting !
I didn't know it was possible do this kind of stuff (I'm not yet super familiar with Lua itself). :)
User avatar
zorg
Party member
Posts: 3468
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: OpenGL events in Love for RenderDoc

Post by zorg »

idbrii wrote: Tue Jan 04, 2022 5:18 pm That's really cool!

You could even add a wrapper func to make adding markers around functions easier:

Code: Select all

Call = function( self, name, fun, ... )
	    GL:PushEvent( name )
	    fn(...)
	    GL:PopEvent()
	end


function love.draw()
	GL:Call( "gameplay", mygame.draw, mygame ) -- calls mygame:draw()
	GL:Call( "ui", draw_ui ) -- calls draw_ui()
end
A bit of a warning, since you're missing part of the code at the very beginning... i think, anyway:
Assuming the Call function is part of the GL table, you really need to show whether you're using a dot or a colon with it, since in the latter case only, you don't need to have "self" be an explicit parameter (it'll implicitly be available as the first passed parameter from a call)
... then again, the inside of the function should also use self instead of GL too, otherwise, why make it use colon in the first place? :3

Code: Select all

GL.Call = function(self, name, fun, ...) -- since it's explicit, you don't really need to call it self, only if you want to.
  self:PushEvent(name)
  fn(...)
  self:PopEvent()
end

--[[
function GL:Call(name, fun, ...) -- this works too, since it's functionally equivalent to the above.
  self:PushEvent(name)
  fn(...)
  self:PopEvent()
end
--]]

function love.draw()
	GL:Call("gameplay", mygame.draw, mygame ) -- calls mygame:draw() -> which is functionally equivalent to mygame.draw(mygame)
	GL:Call("ui", draw_ui ) -- calls draw_ui()
end
Last edited by zorg on Fri Jan 07, 2022 9:16 am, edited 1 time in total.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
idbrii
Prole
Posts: 34
Joined: Sat Jun 12, 2021 5:07 am

Re: OpenGL events in Love for RenderDoc

Post by idbrii »

Oh yeah, that was meant to be another entry in the table.

Shouldn't that latter example (the commented out one) be:

Code: Select all

function GL:Call(name, fun, ...) -- this works too, since it's functionally equivalent to the above.
    self:PushEvent(name)
    fn(...)
    self:PopEvent()
end
GL:Call = function(name, fun, ...) isn't valid lua (in love's 5.1 at least!).
User avatar
zorg
Party member
Posts: 3468
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: OpenGL events in Love for RenderDoc

Post by zorg »

idbrii wrote: Fri Jan 07, 2022 6:02 am Shouldn't that latter example (the commented out one) be:
<code>
GL:Call = function(name, fun, ...) isn't valid lua (in love's 5.1 at least!).
Yep, it should! Edited my post, thanks.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
Froyok
Prole
Posts: 29
Joined: Tue Nov 16, 2021 4:53 pm
Contact:

Re: OpenGL events in Love for RenderDoc

Post by Froyok »

Little update: I noticed that Love sometimes doesn't immediately sends the draw command. So calling PopEvent() might lead to incorrect results in RenderDoc because the actual draw in the code happens after the end of the event.
This can be solved by calling love.graphics.flushBatch() before the PopEvent() call. To be used with care to avoid affecting your performances. :)
User avatar
Froyok
Prole
Posts: 29
Joined: Tue Nov 16, 2021 4:53 pm
Contact:

Re: OpenGL events in Love for RenderDoc

Post by Froyok »

Time has passed, and with the help of Sasha I refined my original code (which wasn't working on Windows and was bothering me). The new snippet below now works on both Linux and Windows. (No idea about Mac since I cannot test it myself.)

The main difference is that I now wrapped a bit better the code into a module and also switched away from loading the OpenGL library to instead use the SDL functionality to query the functions address and call it them.

Bonus: I also added the IsExtensionSupported() function to see if specific OpenGL extensions are supported by the host.

olg.lua

Code: Select all

local FFI = require( 'ffi' )
local OPENGL = {}
OPENGL.GL = {}
OPENGL.SDL = FFI.os == "Windows" and FFI.load("SDL2") or FFI.C

function OPENGL.Init()
	local Definitions = [[
		//---------------------
		// OpenGL
		//---------------------
		typedef char GLchar;
		typedef int GLsizei;
		typedef unsigned int GLuint;
		typedef unsigned int GLenum;

		// void glPushDebugGroup( GLenum source, GLuint id, GLsizei length, const GLchar *message );
		typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC) (GLenum source, GLuint id, GLsizei length, const GLchar *message);

		// void glPopDebugGroup( void );
		typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC) (void);

		//---------------------
		// SDL
		//---------------------
		typedef bool SDL_bool;
		SDL_bool SDL_GL_ExtensionSupported( const char *extension );
		void* SDL_GL_GetProcAddress( const char *proc );
	]]

	if FFI.os == "Windows" then
		Definitions = Definitions:gsub( "APIENTRYP", "__stdcall *" )
	else
		Definitions = Definitions:gsub( "APIENTRYP", "*" )
	end

	FFI.cdef( Definitions )

	-- https://registry.khronos.org/OpenGL/api/GL/glext.h
	local Names = {
		{ "glPushDebugGroup", "PFNGLPUSHDEBUGGROUPPROC" },
		{ "glPopDebugGroup", "PFNGLPOPDEBUGGROUPPROC" }
	}
	local ProcName = ""
	local GLName = ""

	for i=1, #Names do
		GLName = Names[i][1]
		ProcName = Names[i][2]
		local Function = FFI.cast( ProcName, OPENGL.SDL.SDL_GL_GetProcAddress(GLName) )
		rawset( OPENGL.GL, GLName, Function)
	end
end

function OPENGL.IsExtensionSupported( Name )
	return OPENGL.SDL.SDL_GL_ExtensionSupported( Name )
end

function OPENGL.PushEvent( Message )
	OPENGL.GL.glPushDebugGroup(
		0,
		0,
		string.len( Message ),
		Message
	)
end

function OPENGL.PopEvent()
	OPENGL.GL.glPopDebugGroup()
end

OPENGL.Init()
return OPENGL
Usage is the same as before, simply call Push/Pop to groups you API calls.
A few tricks to be able to make sure your graphic requests get put into the group (since Löve has full control on when command are sent to the GPU by default):
  • Use love.graphics.flushBatch() before calling PopEvent() to force Löve to send out drawing command before the debug group close.
  • Use love.graphics.setCanvas() to switch between canvas and therefore force a flush in case flushBatch is not enough (for example when drawing text, which has its own batching).
These post points have been to used only when necessary (and know what you are doing) otherwise you could break optimizations and impact performance.
In my case I used those to ensure text was drawn at specific time in my rendering process before I went to do other operations.
Post Reply

Who is online

Users browsing this forum: Hugues Ross and 7 guests