love icon indicating copy to clipboard operation
love copied to clipboard

Vulkan Graphics Backend

Open nikeinikei opened this issue 2 years ago • 1 comments

A few months ago I asked on the löve discord what direction löve was aheading in the future, and I got told that eventually a vulkan backend would be desired (I think target is LÖVE 13). I tried to implement a very basic version of this. I got to a point where I am able to draw simple geometric shapes, such that the following code is already working:

function love.run()
	return function()
		love.event.pump()
		for name, a,b,c,d,e,f in love.event.poll() do
			if name == "quit" then
				if not love.quit or not love.quit() then
					return a or 0
				end
			end
			love.handlers[name](a,b,c,d,e,f)
		end
		
		love.graphics.origin()
		love.graphics.clear()
		
		love.graphics.rectangle("line", 20, 30, 300, 200)
		love.graphics.rectangle("fill", 500, 20, 100, 200)
		love.graphics.polygon("line", 50, 300, 70, 310, 110, 350, 60, 360, 20, 340)
		love.graphics.circle("line", 400, 400, 80)
		love.graphics.arc("line", 600, 300, 50, math.pi/8, 3*math.pi/8)

		love.graphics.present()
	end
end

The code is very hacky as I was just going for a "minimal viable product", and most of it is adapted from here https://vulkan-tutorial.com/Introduction. I think before continuing on it would be good to ask for general architecture and design choices, since vulkan is fairly different to opengl in a few ways. Here's the questions that I came up with.

  • I'm on Windows using the lunarg vulkan sdk, I added it as a dependency in the cmake file. I'm not sure how dependencies work on macos or linux.
  • Right now drawing is done asynchronously and submitted on love.graphics.present. I think this works fine for normal drawing, although it might create problems when drawing to a canvas since that assumes drawing in immediate mode?
  • Should multithreaded rendering be supported in the future?
  • I wasn't sure what library to use to compile the shaders programmatically, so right now I'm using direct system calls to glslc. This is obviously not desired, what would the preferred way be?
  • Right now I'm using a custom shader since I was unsure how to adopt the opengl shader. In particular, as far as I understand it you need to have a location for all input and uniform variables for vulkan, which would be fine for the standard shader, however for custom löve shaders you're allowed to specify your own uniform variables without being explicit about the location. What's the solution to this?
  • while debugging I noticed that setMode and unsetMode seems to represent initialization and deinitialization of the graphics api (e.g. creation and deletion of vulkan instance / framebuffers / etc ...) . Is that correct?

nikeinikei avatar Mar 26 '22 17:03 nikeinikei

This is a cool initiative! I haven't had time to look through all of the code yet, but for now here are a couple things:

  • The existing Metal backend probably shares a lot of the same structure as a Vulkan backend would need (in terms of how command buffers, render passes, etc. are done) - and I think a lot of the Metal backend's shader pipeline might be useful for a Vulkan backend as well, since the Metal backend translates from GLSL to SPIR-V (and then to MetalSL). For example these lines do a lot of automatic conversion so local uniforms don't need explicit bindings and don't need to be put into a uniform buffer manually, etc. I even added a bit of code to love's fork of glslang, to allow reflection APIs to see uniform initializers (uniform float foo = 5.0; etc) before glslang converts those uniforms to a UBO (which throws away the initializer). That being said, some parts of love's metal backend are a bit messy right now so I'd be happy to answer questions about parts that aren't intuitive to figure out.
  • Vulkan's memory allocation seems kind of tough to get right, so I figured VMA (which had a new release a few days ago) would probably be a good library to use with love, to make it a lot simpler.

slime73 avatar Mar 28 '22 00:03 slime73

As far as I'm aware almost all functionality has been implemented. There are a few fixme's left, I tried to document them in the code.

If I remember correctly, there are some options in initCapabilities that I did not quite understand (it's the first few from FEATURE_CLAMP_ZERO until FEATURE_FULL_NPOT).

I am also unsure about the implementation of the method getSizedFormat, in particular what to do with the sRGB argument.

I tested it a little bit under android (this just needs a bump of the android sdk version to level 24, since vulkan support was introduced with android 7). As a side note, I had some trouble trying to make it work with an emulator, it should work on real hardware (it does for me).

Most of the development process was done under windows, so that should be the most stable. Building it should be identical to the normal way (with cmake), except the LunarG Vulkan sdk needs to be installed, mainly just for its header files. I don't really have a Linux computer so I didn't get to test it there, however I did manage to make it compile under wsl2 using the cmake build (I'm very unfamiliar with the linux/makefile setup).

I also made a little löve project that very roughly validates some of the core functionality. You can check it out here: https://github.com/nikeinikei/love-graphics-validation. I'm sure there are some edges cases, I did not cover / implement yet.

Things that I did not test yet are stencils, as well as storage and texel buffers (since I did not yet quite figure out the new api for it). A little löve test project for this functionality would be appreciated.

I tried to write it in a way such that it should compile with any vulkan sdk version, and it should also run with graphics drivers that don't have vulkan support yet, since the vulkan functions are dynamically loaded at runtime (which is allowed to fail).

I'm of course open to feedback (I hope I didn't majorly screw something up).

nikeinikei avatar Sep 18 '22 12:09 nikeinikei

Awesome! That's a lot of work!

I haven't looked at the code much yet, but I think once the CI builds all pass I'm inclined to merge this sooner rather than later, and we can work on polish after that. Maybe the priority of the vulkan renderer could be dropped below opengl while that polish work is ongoing (via the rendererOrder list in Graphics.cpp), so other people can continue to use the opengl backend by default until the vulkan one is known to work with a large number of projects.

It can still be tested in that situation with a --renderers vulkan command line parameter, or t.renderers = {"vulkan"} in love.conf.

(for the CI builds, I think you can ignore the AppVeyor one because I'll probably remove it soon anyway. So I think it's just the linux one that's left.)

slime73 avatar Sep 18 '22 13:09 slime73

The AppImage is building now, however when running it in WSL I get execv error: Permission denied. Same thing happens when I try to run the latest 12.0 AppImage (from https://github.com/love2d/love/actions/runs/2931151575). Maybe I'm doing something wrong, I think it would be good if someone else would check it out as well.

nikeinikei avatar Sep 19 '22 12:09 nikeinikei

I was able to run the appimage after extracting, making AppRun executable and rebuilding the AppImage. It boots with "OpenGL" in the title. I then get this error trying to run the validation program:

love: ../../src/libraries/vma/vk_mem_alloc.h:14043: VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo*): Assertion `0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros."' failed.
fish: Job 1, '../LÖVE-x86_64.AppImage .' terminated by signal SIGABRT (Abort)

Some relevant info from vulkaninfo:

GPU0:
VkPhysicalDeviceProperties:
---------------------------
        apiVersion        = 4206786 (1.3.194)
        driverVersion     = 2140209280 (0x7f910080)
        vendorID          = 0x10de
        deviceID          = 0x2191
        deviceType        = PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
        deviceName        = NVIDIA GeForce GTX 1660 Ti
        pipelineCacheUUID = c9703ca3-0682-d39e-4da4-e2011f824af5
GPU1:
VkPhysicalDeviceProperties:
---------------------------
        apiVersion        = 4206796 (1.3.204)
        driverVersion     = 92274689 (0x5800001)
        vendorID          = 0x8086
        deviceID          = 0x3e9b
        deviceType        = PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU
        deviceName        = Intel(R) UHD Graphics 630 (CFL GT2)
        pipelineCacheUUID = 85cacf91-d559-b192-c454-f20e94216859

radgeRayden avatar Sep 19 '22 13:09 radgeRayden

For the AppImage issue, I think I broke something when optimizing the AppImage. I've tracked it in my repo. I'll fix it when I have time, but I think the fix is trivial so PR to my repo is welcome.

MikuAuahDark avatar Sep 19 '22 13:09 MikuAuahDark

I think I know what happened, latest commit should fix it (maybe).

nikeinikei avatar Sep 19 '22 13:09 nikeinikei

Ok, I think I'll probably merge this in now and we can continue to make changes from there.

One thing I'm interested in looking at later is whether we can reduce or remove the need for installing the vulkan SDK separately on Windows systems when building love, since most other parts of the windows build setup are more self-contained (via megasource etc).

slime73 avatar Sep 19 '22 19:09 slime73

For release builds only the vulkan header (<vulkan/vulkan.h>) is needed afaik. For development the sdk is useful for its validation layers.

nikeinikei avatar Sep 19 '22 19:09 nikeinikei

Ah, in that case I wonder if it'd work to move the header into love's source (which would also help avoid issues on linux where the system it's compiled on has an old header resulting in the compiled binary not being able to use newer features on other systems), and then the rest of the SDK could be made optional rather than required.

slime73 avatar Sep 19 '22 19:09 slime73