Coder Social home page Coder Social logo

jadarve / lluvia Goto Github PK

View Code? Open in Web Editor NEW
49.0 7.0 11.0 59.9 MB

A real-time computer vision engine implemented on top of Vulkan API.

Home Page: https://lluvia.ai/

License: Apache License 2.0

C++ 47.57% GLSL 5.49% Python 10.05% Dockerfile 0.19% Lua 15.28% Starlark 5.77% Shell 0.12% C 0.17% Cython 15.36%
vulkan computer-vision robotics gpu-computing gpgpu vulkan-engine engine gpu compute-shaders lluvia

lluvia's Introduction

Low Level Unified VIsion Algorithms (LLUVIA)

lluvia.ai

CircleCI

Lluvia is a computer vision engined designed for real-time applications. It uses the Vulkan API to access the GPU and dispatch computations. Users can describe computations as a compute pipeline where nodes are compute shaders running on the GPU.

Real Time Optical Flow

This demo is for my real-time optical flow algorithm running using Lluvia on a GTX-1080 GPU. For images of 1016x544 resolution, computation take around 1 millisecond to complete.

Supported Platforms

  • Linux.
  • Windows.
  • Android (work in progress).

Try it out

Try the examples in Google Colab. Make sure the runtime is configured to GPU.

Workflow

Lluvia uses a compute graph to organize and schedule computations on the GPU. The development workflow circles around coding and debugging nodes in such a graph until the whole algorithm is built:

  1. The node's inputs, outputs and parameters are described in a Lua script . This description will later be used to instantiate nodes in the graph.

  2. The node's computation in the GPU is coded as a compute shader in Open GL Shading Language (GLSL). Shaders are compiled into SPIR-V intemediate representation for later load into the GPU.

  3. The node's description and compute shader are loaded into Lluvia's runtime. After this, nodes can be instantiated to build the compute graph and be dispatched to the GPU.

From a user perspective, one needs to only care about describing nodes (inputs, outputs, compute shader, etc.) and connecting nodes to form a graph. Lluvia takes care of the low-level details of dispatching the graph for execution onto the GPU. This workflow allows porting the compute graph from one platform to another with ease.

License

Apache 2.0. See LICENSE for more details

lluvia's People

Contributors

0xafbf avatar dependabot[bot] avatar jadarve avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

lluvia's Issues

Utility Methods for Python

The Python library needs more convenience functions for repetitive tasks such as:

  • Creation of Buffers and Images. This includes creation from Numpy arrays.
  • Copy operations (Buffer-Buffer, Image-Buffer). The following is the current way to do it:
    cmdBuffer = session.createCommandBuffer()
    cmdBuffer.begin()
    cmdBuffer.changeImageLayout(img, 'TransferDstOptimal')
    cmdBuffer.copyBufferToImage(stageBuffer, img)
    cmdBuffer.changeImageLayout(img, 'General')
    cmdBuffer.end()
    session.run(cmdBuffer)
  • Give the API a more "dynamic" flavour. For instance, running a compute node directly from the object:
    node = session.readComputeNode(...)
    node.run()
  • Create ComputeNode objects from GLSL code.

createImageViewFromHost() in Python

A new convenience function to reduce the number of lines of code needed to create an ImageView.

Currently, I need to do this:

# GPU memory where the images will be allocated
memory = session.createMemory()
 
# reads the image and transfer it to a ll.Image object (in the GPU)
mouse = ll_util.readSampleImage('mouse')
mouse_gpu = memory.createImageFromHost(mouse)
 
# creates an ImageView used to read the pixels in the GPU shader
in_rgba = mouse_gpu.createImageView(ll.ImageFilterMode.Nearest, ll.ImageAddressMode.ClampToEdge, False, False)

A more convenient way would be:

# GPU memory where the images will be allocated
memory = session.createMemory()
 
# reads the image
mouse = ll_util.readSampleImage('mouse')
 
# creates an ImageView used to read the pixels in the GPU shader
in_rgba = memory.createImageViewFromHost(mouse, ll.ImageFilterMode.Nearest, ll.ImageAddressMode.ClampToEdge, False, False)

Python Unit Tests

Create unit test cases for the Python wrappers.

  • Use Pytest as testing framework
  • Check if it is possible to run the tests through the setup.py script.

Graph API

Define the compute graph API for building complex pipelines.

Inspirations:

  • GStreamer
  • TensorFlow

First examples

Code the first examples using the existing version of the engine to discover how to design the interface for developing real-life compute graphs.

Examples:

  • Image pyramid.
  • Image gradient with separable convolution masks.

Android Wrappers

  • Verify that vulkan.hpp can be used on Android.
  • Create android specific CMakeList files (platforms folder?)
  • Test application.

Github Actions

Publish the python package to Pypi

Use Github actions

Define the API for mapping buffers

  • Consider blocking the memory page.
  • Do the mapping from the buffer object, not the memory one.
  • Return some type of smart pointer of the mapped region.
  • Validate if the buffer is mappable.

Fix Buffer creation from Lua

The script below does not work:

local builder = ll.class(ll.ComputeNodeBuilder)

function builder.newDescriptor() 
    
    local desc = ll.ComputeNodeDescriptor.new()
    
    desc.builderName  = 'Square'
    desc.localShape   = ll.vec3ui.new(32, 1, 1)
    desc.gridShape    = ll.vec3ui.new(1, 1, 1)
    desc.program      = ll.getProgram('Square')
    desc.functionName = 'main'

    desc:addPort(ll.PortDescriptor.new(0, 'in_buffer', ll.PortDirection.In, ll.PortType.Buffer))
    desc:addPort(ll.PortDescriptor.new(1, 'out_buffer', ll.PortDirection.Out, ll.PortType.Buffer))

    return desc
end

function builder.onNodeInit(node)
    
    local in_buffer = node:getPort('in_buffer')

    -- Allocate out_buffer in the same Memory as in_buffer
    local memory = in_buffer.memory
    
    -- TODO: call createBuffer using default usage flags
    -- TODO: overload of createBuffer with in_buffer.usageFlags does not work. Need unsafe version
    local out_buffer = memory:createBuffer(in_buffer.size)
    
    -- bind the output
    node:bind('out_buffer', out_buffer)
    
    -- configure the dispath shapes according to the size of in_buffer
    node:configureGridShape(ll.vec3ui.new(in_buffer.size / 4, 1, 1))

end

ll.registerNodeBuilder('Square', builder)

is there something wrong in CMakeLists?

#################################################

SOURCE FILES SETTINGS

#################################################
add_subdirectory(core)--------------------------->core is not exsit, you means : cpp/core ?
add_subdirectory(tools)
add_subdirectory(samples)

creates the shaders custom target

include (cmake/shaders.cmake)

Now, there are some compilation errors with cmake...............

Python Packages

  • Build Python packages for Linux and Windows.
  • Create releases.

Wiki Pages

Create a first version of the project's wiki.

  • About the project
  • Getting started:
    • Building C++ library.
    • Building Python wrappers.
    • Basic usage C++ and Python

Build on OSX

  • OS-specific compile flags in the repo.
  • Include jobs in Github Actions to validate.
  • Build the C++ library and Python wheel.

Python wrappers

  • Use Cython.
  • Check possibility to automate parts of the generation. Like creating the Cython header files.

Code example

import lluvia.core as ll
import numpy as np

session = ll.createSession()

memory = session.createMemory(ll.MemoryFlags.HostVisible | ll.MemoryFlags.HostCoherent, 4096, False)

buf1 = memory.createBuffer(2048)
buf2 = memory.createBuffer(2048)

program = session.createProgram('add.spv')

# setting all this is boring! maybe could be read from a JSON
nodeDesc = ll.ComputeNodeDescriptor(program, 'main', [32, 1, 1], [32, 1, 1])
nodeDesc.parameters.append(ll.ParameterType.Buffer)
nodeDesc.parameters.append(ll.ParameterType.Buffer)

node = session.createComputeNode(nodeDesc)
node.bind(0, buf1)
node.bind(1, buf2)


# alternative way, read a JSON with all the information
"""
{
    "function": "main",
    "grid_x": 1,
    "grid_y": 1,
    "grid_z": 1,
    "local_x": 1024,
    "local_y": 1,
    "local_z": 1,
    "parameters": [
        "BUFFER",
        "BUFFER"
    ],
    "spirv": "gADABsAAAAZAAAA/QABADgAAQA="
}
"""
node2 = session.readComputeNode('pathToNodeDesc.json', localGroup=[16, 1, 1])
node2.grid = [32, 1, 1] # local workgroup cannot be changed from here
                        # as the node has been compiled.
node2.bind(0, buf1)
node2.bind(1, buf2)

# node2 can be saved to JSON. It contains all the relevant information
ll.writeComputeNode(node2, 'pathToFile.json')


cmdBuffer = session.createCommandBuffer()
cmdBuffer.run(node)

session.run(cmdBuffer)

# convert to numpy array
bufCopy = buf1.toHost([1024], np.dtype.float32)

buf2.fromHost(bufCopy)

Change to generic Objects in ComputeGraph

  • Instead of adding specific containers for buffers, images, etc, use a single container of Object to store them all.
  • Change the JSON format to include a single objects list. Distinguish each object with a type attribute.

Android/Java wrappers

Continuous Integration

  • Check if Travis CI supports running the core tests.
  • Build C++ library.
  • Build Python wrappers.
  • Build Doxygen.
  • Build Python doc (Sphinx?).
  • Run test (C++, Python).

Image objects

  • Check which image formats are usable in compute shaders.
  • Supported image formats. Number of channels and type.
  • Image class.

Nodes library

Add Bazel rules to create node libraries. A node library is a collection of Lua and SPIR-V files packed together and ready to be used within a session.

Instruction for build

Hi @jadarve,

I would like to try lluvia. I tried compiling using CMake without success as some files cannot be found.

Is there any plan on adding build and installation instruction?

Thank you!

Organize glsl folder

Currently, the glsl folder in the repository contains shader code for testing purposes. In the future, this folder will contain general (library) code for the GPU.

Task

  • Move the test shaders to a core/test/glsl folder.
  • Adapt the CMake scripts to support shader compilation from several places.

Mediapipe Calculators

  • Inclue Lluvia as a dependency on mediapipe's bazel scripts.
  • Create calculators to run graphs within mediapipe.

Port Contracts

  • Add more properties to the port descriptor in order to validate proper binding. Examples include:
    • Image channels
    • Image channel type
    • Image view sampled
    • Image view normalized coordinates

For instance, take RGBA2Gray node:

function builder.newDescriptor()

    local desc = ll.ComputeNodeDescriptor.new()

    desc:init(builder.name, ll.ComputeDimension.D2)

    -- TOTHINK: increased port contracts by checking internal attributes of the PortType
    -- For ImageView, check all image attributes + image view attributes.
    desc:addPort(ll.PortDescriptor.new(0, 'in_rgba', ll.PortDirection.In, ll.PortType.ImageView))
    desc:addPort(ll.PortDescriptor.new(1, 'out_gray', ll.PortDirection.Out, ll.PortType.ImageView))

    return desc
end

function builder.onNodeInit(node)

    local in_rgba = node:getPort('in_rgba')

    -- validate in_rgba is actually a rgba8ui image
    -- TODO: remove once port-contracts are implemented
    local err = ll.isValidImage(in_rgba, ll.ChannelCount.C4, ll.ChannelType.Uint8)
    if err ~= nil then
        error(builder.name .. ': error validating in_rgba: ' .. err)
    end

    -- ...
end

The code in the builder, as well as the likelihood of errors, can be reduced if the node internally validates if in_rgba has 4 channels and it's of the correct channel type.

โš ๏ธ Consider also the parameterization of nodes according to channel type and count. For instance, ImagePyramid_r8ui is tailored to 8-bit single-channel images. Any other combination would require a new node definition.

Fix references in class hierarchy

Currently all objects that are created by the Session class hold a reference to it. Instead, hold a reference to the Vulkan resources needed by that object for maintaining its life-cycle. In particual, the vk::Device reference.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.