Giter Site home page Giter Site logo

lukasbanana / llgl Goto Github PK

View Code? Open in Web Editor NEW
1.9K 1.9K 133.0 69.28 MB

Low Level Graphics Library (LLGL) is a thin abstraction layer for the modern graphics APIs OpenGL, Direct3D, Vulkan, and Metal

License: BSD 3-Clause "New" or "Revised" License

CMake 1.17% C 1.68% C++ 83.09% Objective-C++ 5.37% HLSL 0.53% GLSL 0.23% TeX 0.65% Objective-C 1.20% Batchfile 0.19% Metal 0.11% Shell 0.38% Python 1.27% C# 4.15%
d3d11 d3d12 directx metal opengl renderer vulkan

llgl's People

Contributors

autoantwort avatar beldenfox avatar bsaleil avatar duckdoom4 avatar frankxie05 avatar gdianaty avatar germanaizek avatar julianxhokaxhiu avatar lukasbanana avatar nofr1ends avatar raxvan avatar shenmian avatar underdoeg 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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 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  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

llgl's Issues

Move "GenerateMips" to CommandBuffer interface

The GenerateMips functions should be part of the CommandBuffer interface instead of the RenderSystem interface.

Reason:

  • The D3D12 API needs a compute shader to generate MIP-maps, which is dispatched by the ID3D12GraphicsCommandList interface.
  • The Metal API needs a MTLBlitCommandEncoder to generate MIP-maps, which is acquired by the MTLCommandBuffer interface.

Appendix:

  • The Texture::QueryMipLevelSize function should possibly be renamed to Texture::QueryMipExtent, because the name "extent" corresponds better to the nomenclature. Done

License

Hi,

I was checking the License more carefully and I noticed that this library is using none of the well-known licenses. Therefore I was here to actually ask if there is a plan on using anyone of them like MIT, Apache or GPL?

I was planning in using this in one of my projects, but at the current state I'm a bit concerned about consequences in using your code on top of mine.

Thank you in advance for any clarification given.

Is C# wrapper cross-platform?

It seems like current C# wrapper using C++/CLI. (Correct me if I'm wrong)
Isn't C++/CLI only supported on Windows platform for now? Microsoft introduced C++/CLI support to .NET Core 3 but unfortunately it is only working on Windows.

PInvoke is the only option right now for cross-platform C# binding.

Build Script Rundown?

I'm mulling over the cmakelists file to see how I can port it over to premake5 for my own personal project, but I was wondering if you guys could give me a rundown on the build flags and such that need to be set in order to build it properly. Since the library can only get the full range of features when built as a shared library, I am definitely going to be doing that at least, but is there anything else I shod be aware of? Any gotchas or quirks I should know about?

Simplify texture descriptor

This is a concept for a new and simplied TextureDescriptor that applies to the new rendering APIs. Instead of having multiple nested structures for 1D, 2D, 3D, 2DMS and Cube textures, there is only one descriptor for all types of texutres:

struct TextureDescriptor {
    TextureType   type    = TextureType::Undefined;
    Format        format  = Format::Undefined;
    long          flags   = TextureFlags::Default;
    std::uint32_t width   = 1;
    std::uint32_t height  = 1;
    std::uint32_t depth   = 1;
    std::uint32_t layers  = 1;
    std::uint32_t samples = 1;
};

The member bool TextureDescriptor::Texture2DMS::fixedSamples can be replaced by a new flags entry to the TextureFlags enum.

Usage example:

// 1D array texture
LLGL::TextureDescriptor myTex1DDesc;
myTex1DDesc.type   = LLGL::TextureType::Texture1DArray;
myTex1DDesc.width  = 128;
myTex1DDesc.layers = 64;
auto myTex1D = myRenderer->CreateTexture(myTex1DDesc);

// Cube texture
LLGL::TextureDescriptor myTexCubeDesc;
myTexCubeDesc.type   = LLGL::TextureType::TextureCube;
myTexCubeDesc.width  = 32;
myTexCubeDesc.height = 32; // must be equal to width!
myTexCubeDesc.layers = 1; // automatically multiplied by 6 (one for each cube face)
auto myTexCube = myRenderer->CreateTexture(myTexCubeDesc);

TODO List

This is an overview of some major bullet points for improvements or unimplemented features in LLGL:

OpenGL Renderer:

  • Multi-threading: Deferred command buffer is implemented. The JIT compiler is only experimental and should be disabled for client code.

Vulkan Renderer:

  • Resource state transitioning: This is very fragile and needs improvement.
  • Memory allocator: I'm still uncertain whether to implement a better memory allocator myself as builtin part of LLGL, or simply switch over to AMD's publicly available VulkanMemoryAllocator. I want to keep the number of external dependencies as low as possible, but regarding Vulkan features it's probably better to use those libraries.
  • SPIR-V reflection: I started with a custom SPIR-V parser whose only external dependency is SPIRV-Headers. This either needs to be extended or also switched to another library. SPIRV-Reflect from Google seems promising.
  • Multi-sampling support: There is currently no multi-sampling support for the Vulkan backend.
  • Conditional rendering: Vulkan needs the VK_EXT_conditional_rendering extension for conditional rendering, but it hasn't been implemented in LLGL yet.
  • Stream outputs: Not implemented yet (see SetStreamOutputBuffer etc.). Requires VK_EXT_transform_feedback Vulkan extension.
  • Push constants: The new interface of the SetUniform/ SetUniforms functions in CommandBuffer interface is the way to go for Vulkan's "push constants".
  • Multi-threading: Implemented for primary command buffers.

D3D11 Renderer:

  • Multi-threading: Implemented via deferred context.

D3D12 Renderer:

  • Dynamic buffer updates: This must be implemented first. There is currently no proper support to update buffers during command encoding, i.e. CommandBuffer::UpdateBuffer simply does not work correct for the D3D12 backend. A Ring-Buffer implementation may be useful for this purpose.
  • MIP-map generator: I started with a D3D12MipGenerator class to generate MIP-maps with a compute shader, like the DX12 example illustrates. Before that, GenerateMips function must be moved from RenderSystem to CommandBuffer interface, or RenderSystem may keep it as a secondary function to be used outside of command encoding.
  • Resource state transitioning: This is very fragile and needs improvement.
  • Stream outputs: Implemented (see SetStreamOutputBuffer etc.).
  • Multi-threading: Implemented for primary command buffers.

Metal Renderer:

  • Tessellation example: While tessellation is generally supported, the "Tessellation" example does not work with the Metal backend, because it works fundamentally different in Metal. There are effectively only three shader units: vertex, fragment, and compute. The TessControl shader must be merged into the 'vertex function', and the TessEvaluation shader must be implemented as a 'kernel function'.
  • Fences: Fences are currently not implemented in the Metal backend.
  • Multi-threading: Implemented for primary command buffers.
  • Multi-submit: Implemented and tested.

MacOS Platform:

  • Window event handling and resize: The envent handling in the MacOS implementation of the Window interface could be improved. Especially resiying the window does not work properly.

C# Wrapper:

  • The C# wrapper is now partially auto-generated and uses P/Invoke instead of C++/CLI.

Further unimplemented/incomplete functions:

  • LinuxDisplay::SetDisplayMode
  • LinuxDisplay::ResetDisplayMode
  • LinuxDisplay::ShowCursor
  • LinuxDisplay::IsCursorShown
  • LinuxWindow::SetDesc
  • LinuxWindow::GetDesc
  • D3D12CommandBuffer::ClearAttachments
  • D3D12CommandBuffer::CopyBuffer (resource transition/barrieres)
  • D3D12CommandBuffer::CopyTexture (resource transition/barrieres)

Future plans:

  • Replace CommandBuffer::ResetResourceSlots: This function is only used to circumvent an issue with resource binding with older APIs that do not support descriptor heaps/sets natively. Binding a resource for writing (e.g. a render target) requires the same resource to not be bound at any other binding point for reading. It's not trivial to keep track of each resource where it is bound (potentially at multiple points) in an efficient manner. The ResetResourceSlots function is used so the client programmer can manually unbind resources before binding it for writing (or as UAV in D3D terms). With LLGL having the concept of ResourceHeap, this manual unbinding should not be a requirement. Finding an efficient way to detect resources that need to be unbound is crucial ... and not easy :)
  • Better object management in RenderSystem: The Release functions should not delete the objects immediately. Instead, RenderSystem should keep track which objects are still used. For instance, a released Texture object that is still in use in another ResourceHeap should only be deleted when the last ResourceHeap or Texture-view that refers to it is deleted (via reference counter).

Refactor "SetVertexBufferArray" to "SetVertexBuffers"

The idea is to get rid of all ...Array interfaces as well as Set...Array functions in the CommandBuffer interface. Instead, the new ResourceHeap interface should be used, which complies with the new rendering APIs. The SetVertexBufferArray function should be refactored to the following:

void SetVertexBuffers(std::uint32_t numVertexBuffers, Buffer* const * vertexBuffers);

For all renderers (except OpenGL) this can be trivially implemented by using an uninitialized local C-style array that is passed to the native renderer API like this:

void D3D11CommandBuffer::SetVertexBuffers(
    std::uint32_t  numVertexBuffers,
    Buffer* const* vertexBuffers)
{
    /* Uninitialized local arrays are trivially constructed on the stack */
    ID3D11Buffer* buffers[g_maxVertexBuffers];
    UINT strides[g_maxVertexBuffers];
    UINT offsets[g_maxVertexBuffers];

    /* Convert vertex buffer array to native parameters */
    numVertexBuffers = std::min(numVertexBuffers, g_maxVertexBuffers);
    for (std::uint32_t i = 0; i < numVertexBuffers; ++i)
    {
        auto vertexBufferD3D = LLGL_CAST(D3D11VertexBuffer*, vertexBuffers[i]);
        buffers[i] = vertexBufferD3D->GetNative();
        strides[i] = vertexBufferD3D->GetStride();
        offsets[i] = 0;
    }

    /* Pass local arrays to native API */
    context_->IASetVertexBuffers(0, numVertexBuffers, buffers, strides, offsets);
}

This works analogous to the SetViewports and SetScissors functions.

For OpenGL, however, this requires a little more effort. The idea is to convert the GLVertexArrayObject member variable in GLVertexBuffer into shared pointer. Then all vertex buffers used in a call to SetVertexBuffers refer to the same VAO. When these buffers are used in the SetVertexBuffers function for the first time, this VAO is created 'on the fly'. When they are used together once more, this VAO can be reused. This allows to dynamically change the vertex buffers without letting the client programmer manage a BufferArray object.

Caveat:
To make a buffer usable for multiple sets of vertex buffers, each GLVertexBuffer must hold a container of shared pointers. Otherwise, some VAOs could be created and deleted every frame :-(
So either each GLVertexBuffer needs this list, which must be iterated for each buffer used in the SetVertexBuffers function, or some sort of hash map must be involved to quickly find the correct VAO for a set of buffers.

Alternative 1:
Get rid of all ...Array interfaces except of BufferArray (but only for vertex buffer arrays).

Alternative 2:
Both SetVertexBufferArray and SetVertexBuffers are supported. The former one is supported for optimization purposes and the latter one is supported for flexibility purposes.

Update (12/07/2018):
Maybe the GL_ARB_vertex_attrib_binding extension can help here.

Internal memory allocation callbacks

Does LLGL have support for host memory allocations callbacks ? Such as VkAllocationCallbacks for vulkan? How about other apis (dx, ogl, etc) and other library allocations ?

If not, is this planned ?

ShaderReflection without shader initialization

So I'm trying to generate Shader/Material/Resources using shader reflection. I already did this before and had an hlsl solution.

Looking through the code I found out you can actually use ShaderProgram::Reflect() to do exactly that, but now I have a minor problem.

I need to create and initialize a shader whenever I want to use shader reflection to create the actual shader.

From looking through the code I can see that you only need the shader byte code which is generated using the shader compiler.

Unfortunately I don't see any way to just compile the shader and use the byte code to reflect.

Am I missing something? If not could you make this possible by splitting the compiler and reflection from the shader resource?

Question: LLGL_OpenGL , WriteTexture

Hi. I found that when write compressed texture to Cubemap texture with extension GL_ARB_direct_state_access you call glCompressedTextureSubImage3D function just like when it is not compressed you call glTextureSubImage3D function. And I checked that why you call this function in page : https://www.khronos.org/opengl/wiki/Cubemap_Texture.
It says : "Uploading pixel data for cubemaps is somewhat complicated. If OpenGL 4.5 or ARB_direct_state_access is available, then glTexSubImage3D can be used to upload face data for a particular mipmap level. The third dimension values for glTexSubImage3D represents which face(s) to upload data to. The faces are indexed in the following order: ..."

But when I use the compressed type write function it seems write nothing to the texture(all is black). So I want to know whether the glCompressedTextureSubImage3D function can use for cubemap texture just as same as the glTextureSubImage3D function ?

Copy, Clear, Map

Hardware accelerated functions for copy, clear and map Buffer and Texture objects should be added to the RenderSystem and CommandBuffer interface.

Concept for Buffer:

// Todo (use 'glClearBufferSubData' for GL renderer)
void CommandBuffer::FillBuffer(
    Buffer&         dstBuffer,
    std::uint64_t   dstOffset,
    std::uint32_t   value,
    std::uint64_t   fillSize
);

// Done
void CommandBuffer::CopyBuffer(
    Buffer&         dstBuffer,
    std::uint64_t   dstOffset,
    Buffer&         srcBuffer,
    std::uint64_t   srcOffset,
    std::uint64_t   size
);

// Done
void* RenderSystem::MapBuffer(Buffer& buffer, const BufferCPUAccess access);
void RenderSystem::UnmapBuffer(Buffer& buffer);

Progress overview:

  • FillBuffer
  • CopyBuffer
  • CopyBufferFromTexture
  • MapBuffer/ UnmapBuffer
  • CopyTexture
  • CopyTextureFromBuffer

CommandBuffer Encoding

Start/stop of command recording must be part of the CommandBuffer interface instead of the CommandQueue interface. The submission of the command buffers to the command queue should also become explicit.

Before:

cmdQueue->Begin(*cmdBuffer);
/* ... */
cmdQueue->End(*cmdBuffer);
context->Present();

After:

cmdBuffer->Begin();
/* ... */
cmdBuffer->End();
cmdQueue->Submit(*cmdBuffer);
context->Present();

Command recording is independent of the command queue and must be part of the CommandBuffer interface, to allow multi-threaded command recording (aka. encoding) in a later version of LLGL.

The CommandQueue interface should also get a secondary Submit function to submit multiple command buffers at once:

void Submit(
    std::size_t           numCommandBuffers,
    CommandBuffer* const* commandBuffers
);

All of this is a preparation for better support of encoding multiple command buffers to benefit from the latest rendering APIs.

Add "RenderPass" interface

Metal has no function to clear the framebuffer attachments on demand. They can only be cleared when a new render-pass begins.

The same functionality is provided in Vulkan, even though it provides the functionality to clear the color attachments on demand, too.

The new RenderPass interface will be used for the GraphicsPipelineDescriptor structure instead of the RenderTarget interface to specfiy the color attachment formats.

Proposal:

enum class AttachmentLoadOp {
    Undefined, // Vulkan: VK_ATTACHMENT_LOAD_OP_DONT_CARE
    Load,      // Vulkan: VK_ATTACHMENT_LOAD_OP_CLEAR
    Clear,     // Vulkan: VK_ATTACHMENT_LOAD_OP_CLEAR
};

enum class AttachmentStoreOp {
    Undefined, // Vulkan: VK_ATTACHMENT_STORE_OP_DONT_CARE
    Store,     // Vulkan: VK_ATTACHMENT_STORE_OP_STORE
};

struct AttachmentFormatDescriptor {
    Format            format  = Format::Undefined;
    AttachmentLoadOp  loadOp  = AttachmentLoadOp::Undefined;
    AttachmentStoreOp storeOp = AttachmentStoreOp::Undefined;
};

struct RenderPassDescriptor {
    std::vector<AttachmentFormatDescriptor> colorAttachments;  // e.g. Format::RGBA8UNorm
    AttachmentFormatDescriptor              depthAttachment;   // e.g. Format::D24UNormS8UInt
    AttachmentFormatDescriptor              stencilAttachment; // e.g. Format::D24UNormS8UInt
};

struct GraphicsPipelineDescriptor {
    ShaderProgram*  shaderProgram  = nullptr;
    PipelineLayout* pipelineLayout = nullptr;
    RenderPass*     renderPass     = nullptr;
    /* ... */
};

class CommandBuffer {
    /* ... */
    void BeginRenderPass(
        RenderPass&       renderPass,
        RenderTarget&     renderTarget,
        std::uint32_t     numClearValues = 0,
        const ClearValue* clearValues    = nullptr
    );
    void EndRenderPass();
};

The render pass will replace the SetRenderTarget functions and RenderContext will finally inherit from RenderTarget:

Before:

myCmdBuffer->SetClearColor({ 1.0f, 0.0f, 0.0f, 1.0f });

myCmdBuffer->SetRenderTarget(*myRenderTarget);
myCmdBuffer->Clear(LLGL::Color);
/* ... */

myCmdBuffer->SetRenderTarget(*myRenderContext);
myCmdBuffer->Clear(LLGL::Color);
/* ... */

myRenderContext->Present();

After:

LLGL::ClearValue clearValue;
clearValue.color = { 1.0f, 0.0f, 0.0f, 1.0f };

myCmdBuffer->BeginRenderPass(*myRenderPass, *myRenderTarget, 1, &clearValue);
/* ... */
myCmdBuffer->EndRenderPass();

myCmdBuffer->BeginRenderPass(*myRenderPass, *myRenderContext, 1, &clearValue);
/* ... */
myCmdBuffer->EndRenderPass();

myRenderContext->Present();

compile OpenGLES for IOS using CMake

i was implementing a es version in IOS project, i met a compile problem ;
build pipeline1
cmake -> ios xcproject -> using xcode build -> succeed

build pipeline2
cmake -> default project -> using cmake build -> failed

while building LLGL ,it succeed , and building LLGL_ES3 , it failed,

my error:
ๅฑๅน•ๅฟซ็…ง 2019-03-11 11 37 31

SDL, X11 and GLFW backends?

Hello. Will support for SDL, X11 (Linux/Unix), and GLFW backends window support? Also, will support of Vulkan API? I very wait for Vulkan API support, as DirectX 12 too.

Hard to run examples in Mac

hello lukas, i found LLGL hard to run example on Mac xcode.

  1. use "MACOSX_BUNDLE" to generate examples, but i don't konw how to read .vert .frag in app's contents folder... pretty sad. i just change that to default, then use absolute path to read them.
  2. when i choose metal to render, just can't find file
    [[NSString alloc] initWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil] return nil.
    i guess U have some good ideas to solving them.

Triangle Program Reimplementation Not Working

So I've been using the basic triangle program as my baseline for ensuring all of the systems I'm implementing for Firestorm are working properly, and I'm hitting a bit of a snag. Where it used to render the triangle properly it is now simply showing me a black screen. I've looked at the source code for the existing Triangle program and everything seems to match up, however my version is not rendering the triangle for whatever reason.

I do have a few layers of abstraction in my implementation, however outside of the asynchronous loading of the shader resources (using std::async at the moment and having it simply load up the source code for it to be compiled on the main thread as seen in the line resource->Compile({vertexFormat});), everything is fairly straightforward.

The source code for the base application class, the program's implementation of that class, and the ResourceMgr class as well as the associated ShaderLoader class that runs the asynchronous loading are all attached. The ShaderResource class is also included so that you can see how I'm loading up the shader resources.

To be honest, this issue has me stumped as I'm still a newbie when it comes to graphics programming and I would really appreciate anyone's assistance in this matter. The shader itself is simple and the differences between the existing triangle shader and the version that's implied by the code here came about as part of my debugging. It wasn't working with the existing version of the shader either. Vector2 and Vector4 are both simple structs with float x, y and float x,y,z,w as you'd expect. Nothing weird going on in there except for a few explicit constructors for Vector2 to take in either a Vector3 or Vector4 for initialization data.

class Vector2
{
public:
	Vector2(float x = 0.0f, float y = 0.0f);
	explicit Vector2(const Vector3& v);
	explicit Vector2(const Vector4& v);

	float x, y;
};

class Vector3
{
public:
	Vector3(float x = 0.0f, float y = 0.0f, float z = 0.0f);
	Vector3(const Vector2& v2d, float z = 0.0f);
	explicit Vector3(const Vector4& v2d);

	float x, y, z;
};

class Vector4
{
public:
	Vector4(float x = 0.0f, float y = 0.0f, float z = 0.0f, float w=0.0f);
	Vector4(const Vector2& v, float z = 0.0f, float w = 0.0f);
	Vector4(const Vector3& v, float w = 0.0f);

	float x, y, z, w;
};

FirestormTriangleProgramIssues.zip

Compilation problems with Vulkan Backend on Windows Platform

I found two problems with the vulkan rendering backend on Windows.

  1. The library files under windows are not longer in the Bin folder instead they are in the Lib(32) folder.
  2. Also I get unresolved external symbols when compiling:
    VKCommandBuffer.cpp.obj : error LNK2019: unresolved external symbol _vkCmdBeginTransformFeedbackEXT@20 referenced in function "public: virtual void __thiscall LLGL::VKCommandBuffer::BeginStreamOutput(unsigned int,class LLGL::Buffer * const *)" (?BeginStreamOutput@VKCommandBuffer@LLGL@@UAEXIPBQAVBuffer@2@@Z) VKCommandBuffer.cpp.obj : error LNK2019: unresolved external symbol _vkCmdEndTransformFeedbackEXT@20 referenced in function "public: virtual void __thiscall LLGL::VKCommandBuffer::EndStreamOutput(void)" (?EndStreamOutput@VKCommandBuffer@LLGL@@UAEXXZ) VKCommandBuffer.cpp.obj : error LNK2019: unresolved external symbol _vkCmdBeginConditionalRenderingEXT@8 referenced in function "public: virtual void __thiscall LLGL::VKCommandBuffer::BeginRenderCondition(class LLGL::QueryHeap &,unsigned int,enum LLGL::RenderConditionMode)" (?BeginRenderCondition@VKCommandBuffer@LLGL@@UAEXAAVQueryHeap@2@IW4RenderConditionMode@2@@Z) VKCommandBuffer.cpp.obj : error LNK2019: unresolved external symbol _vkCmdEndConditionalRenderingEXT@4 referenced in function "public: virtual void __thiscall LLGL::VKCommandBuffer::EndRenderCondition(void)" (?EndRenderCondition@VKCommandBuffer@LLGL@@UAEXXZ)

Texture views

Add the following functions to support texture resource views:

class RenderSystem {
    // Creates a resource view for the source texture
    // using the same <Texture> interface for seamless interoperability.
    Texture* CreateTextureView(Texture& sourceTexture, const TextureViewDescriptor& desc);
    /* ... */
};
class Texture {
    // Returns true if this texture is a resource view and not the original texture
    bool IsResourceView() const;
    /* ... */
};

This will also replace the secondary version of the GenerateMips function (which currently generates resource views on demand).

For OpenGL, this is only supported if the GL_ARB_texture_view extension is available on the host platform.
For D3D11, D3D12, and Vulkan there is a respective resource view interface (e.g. VkImageView or ID3D11ShaderResourceView).
For Metal, the same interface MTLTexture is used for both textures and texture-views.

Only the debug layer will keep track of all resource views that refer to a source texture. When a source texture is released, and any of its resource views is still used, the debug layer will throw an error message.

EDIT:
Instead of making texture views part of the Texture interface, they are probably better suited for the ResourceHeap interface. Also simplifies the way of creating multiple different views of the same texture object.

Replace buffer type by bind flags

Maybe the enumeration LLGL::BufferType should be replaced by LLGL::BufferFlags with additional entries to specifiy one or more purposes for binding a buffer.
This will require a lot of refactoring especially in the D3D11 backend because there are multiple sub-classes of D3D11Buffer, e.g. D3D11ConstantBuffer.

This is especially helpful with the upcoming interface for indirect draw/dispatch commands. The enum BufferType and member BufferDescriptor::type will be replaced by three new enums:

LLGL::BufferDescriptor myDesc;
myDesc.size           = /* ... */;
myDesc.bindFlags      = LLGL::BindFlags::RWStorageBuffer | LLGL::BindFlags::IndirectBuffer;
myDesc.cpuAccessFlags = LLGL::CPUAccessFlags::ReadWrite;
myDesc.miscFlags      = LLGL::MiscFlags::DynamicUsage;
auto myBuffer = myRenderer->CreateBuffer(myDesc);

All these flags will also be available in the TextureDescriptor struct, but here the TextureType enum and type member will remain.

Because the dedicated sub-classes for vertex buffers typically have more payload than the other buffers, the new sub-class hierarchy could look like this:

Buffer
 |- GLBuffer
 |   `- GLBufferWithVAO (for vertex buffers)
 |- D3D11Buffer
 |   `- D3D11BufferWithRV (for storage buffers)
 |- ...
...

Indirect draw/dispatch commands

Indirect draw and dispatch commands as well as their respective buffer types should be added.

Concept:

enum class BufferType {
    Vertex,
    Index,
    Constant,
    Storage,
    StreamOutput,
    DrawIndirect,     // New buffer type for indirect draw commands
    DispatchIndirect, // New buffer type for indirect dispatch commands
};

For the case the rendering APIs have slightly different buffer formats for these commands, individual structures for each backend must be provided. Otherwise, a uniform structure can be used:

struct DrawIndirectFormat {
    std::uint32_t numVertices;
    std::uint32_t numInstances;
    std::uint32_t firstVertex;
    std::uint32_t firstInstance;
};
struct DrawIndexedIndirectFormat {
    std::uint32_t numIndices;
    std::uint32_t numInstances;
    std::uint32_t firstIndex;
    std::int32_t  vertexOffset;
    std::uint32_t firstInstance;
};
struct DispatchIndirectFormat {
    std::uint32_t groupSizeX;
    std::uint32_t groupSizeY;
    std::uint32_t groupSizeZ;
};

The functions in the CommandBuffer interface may look like this:

void DrawIndirect(Buffer& buffer, std::uint64_t offset);
void DrawIndexedIndirect(Buffer& buffer, std::uint64_t offset);
void DispatchIndirect(Buffer& buffer, std::uint64_t offset);

The parameter buffer specifies a buffer that contains the draw/dispatch command arguments and the parameter offset specifies an aligned byte offset within the buffer for command arguments.

Progress overview:

  • DrawIndirect
  • DrawIndexedIndirect
  • DispatchIndirect

Add *optional* resolve attachments to "RenderTarget" interface

Instead of automatic generation of multi-sampled attachments that get resolved into the actual render target attachments, the user should be able to specify their custom multi-sampled textures. For OpenGL, a Texture that has not the BindFlags::Sampled flag bit, a renderbuffer should be used internally, which is currently only done in GLRenderTarget for the auto.-generated multi-sampled attachments. This feature should be optional, though, and not required for convenience.

Before:

struct RenderTargetDescriptor
{
    const RenderPass*                   renderPass          = nullptr;
    Extent2D                            resolution;
    std::uint32_t                       samples             = 1;
    bool                                customMultiSampling = false;
    std::vector<AttachmentDescriptor>   attachments;
};

After:

struct RenderTargetDescriptor
{
    const RenderPass*       renderPass                      = nullptr;
    Extent2D                resolution;
    AttachmentDescriptor    colorAttachments[8];
    AttachmentDescriptor    resolveAttachments[8];
    AttachmentDescriptor    depthStencilAttachment;
};

The samples parameter is taken from the specified (or default) render pass, i.e. RenderPassDescriptor::samples.

Refactor shader interface

Shader and ShaderProgram interfaces should be refactored to follow the 'descriptor at creation time' principle.
Attaching, detaching, and re-compiling shaders does not work properly for Direct3D and Vulkan.
Moreover, it has only been adopted from the deprecated interface of OpenGL (like with glAttachShader).

In the new interface, the shaders must be compiled at creation time:

enum class ShaderSourceType {
    CodeString,   // Refers to <const char[sourceSize + 1]> describing shader code (with '\0')
    CodeFile,     // Refers to <const char[sourceSize + 1]> describing shader code file (with '\0')
    BinaryBuffer, // Refers to <const char[sourceSize]> describing shader binary
    BinaryFile    // Refers to <const char[sourceSize + 1]> describing shader binary file (with '\0')
};

struct ShaderDescriptor {
    ShaderDescriptor();
    ShaderDescriptor(
        ShaderType  type,
        const char* source,
        std::size_t sourceSize = 0
    );
    ShaderDescriptor(
        ShaderType  type,
        const char* source,
        std::size_t sourceSize,
        const char* entryPoint,
        const char* profile
    );
    
    struct StreamOutput {
        StreamOutputFormat format;
    };
    
    ShaderType       type         = ShaderType::Undefined;
    const char*      source       = nullptr; // points to the source code or filename
    std::size_t      sourceSize   = 0;       // length of the source (excluding a null terminator!)
    ShaderSourceType sourceType   = ShaderSourceType::CodeFile;
    const char*      entryPoint   = nullptr;
    const char*      profile      = nullptr; // previously called target, but profile fits better
    long             flags        = 0;
    StreamOutput     streamOutput;
};

struct GraphicsShaderProgramDescriptor {
    std::vector<VertexFormat> vertexFormats;
    Shader*                   vertexShader         = nullptr;
    Shader*                   tessControlShader    = nullptr;
    Shader*                   tessEvaluationShader = nullptr;
    Shader*                   geometryShader       = nullptr;
    Shader*                   fragmentShader       = nullptr;
};

struct ComputeShaderProgramDescriptor {
    Shader* computeShader = nullptr;
};

class RenderSystem {
    Shader* CreateShader(const ShaderDescriptor& desc);
    ShaderProgram* CreateShaderProgram(const GraphicsShaderProgramDescriptor& desc);
    ShaderProgram* CreateShaderProgram(const ComputeShaderProgramDescriptor& desc);
    /* ... */
};

Functions that will be removed from the interfaces:

class Shader {
    bool Compile(const std::string&, const ShaderDescriptor&);
    bool LoadBinary(std::vector<char>&&, const ShaderDescriptor&);
    /* ... */
};
class ShaderProgram {
    void AttachShader(Shader&);
    void DetachAll();
    bool LinkShaders();
    void BuildInputLayout(std::uint32_t, const VertexFormat*);
    /* ... */
};

Functions that will be added to the interfaces:

class Shader {
    bool HasErrors() const;
    /* ... */
};
class ShaderProgram {
    bool HasErrors() const;
    /* ... */
};

Example usage:

LLGL::GraphicsShaderProgramDescriptor myGfxProgramDesc;
{
    myGfxProgramDesc.vertexFormats  = { myVertexFormat };
    myGfxProgramDesc.vertexShader   = myRenderer->CreateShader({ LLGL::ShaderType::Vertex,   "myVertexShader.vert"   });
    myGfxProgramDesc.fragmentShader = myRenderer->CreateShader({ LLGL::ShaderType::Fragment, "myFragmentShader.frag" });
}
auto myShaderProgram = myRenderer->CreateShaderProgram(myGfxProgramDesc);

D3D11: Depth texture support

The D3D11 renderer currently does not support depth textures (i.e. format TextureFormat::D32 and TextureFormat::D24S8 from the latest "vulkan"-branch).

Notes:
Depth textures in D3D11 can have more than one MIP-map, but the GenerateMips function cannot be used with them when they have the bind flag D3D11_BIND_DEPTH_STENCIL.
This, however, is only required when the depth-texture is used as depth-stencil attachment (i.e. LLGL::TextureFlags::DepthStencilAttachmentUsage).

ResourceHeap should provide multiple sets of consecutive descriptors

ResourceHeap currently only supports one set of resource views that are always bound at once.
It should be extended to provide a functionality similar to the descriptor heap in D3D12, which stores an arbitrary number of descriptors in one heap.
The number of resources that are bound with SetResourceHeap will depend on the pipeline layout of the actively bound pipeline state.
SetResourceHeap should then have an optional parameter to specify the first resource within the heap.
Moreover, the parameter firstSet should be moved into BindingDescriptor structure:

void SetResourceHeap(
    ResourceHeap&           resourceHeap,
    std::uint32_t           firstResource   = 0,
    const PipelineBindPoint bindPoint       = PipelineBindPoint::Undefined
);

Miscellaneous for v0.03

Miscellaneous scheduled features:

  • Secondary GenerateMips function with parameters for a range of MIP-levels and array slices.
  • Extend D3D11StateManager to avoid redundant calls to VSSetShader etc.
  • Multi-sampling support for GL renderer on Linux.
  • Multi-sampling support for D3D12 renderer.
  • Multi-sampling support for Vulkan renderer.
  • Depth texture support in D3D11 renderer.
  • Add interface for indirect draw/dispatch commands with respective BufferType entries.
  • Better support of UAV and SRV distinction for D3D11 renderer.
  • CPU side fragment output binding for OpenGL renderer with ShaderDescriptor::fragment::outputAttribs.
  • Default image initialization of stencil values.

Buffer updates in CommandBuffer

The RenderSystem should keep its functions to read and write buffers. However, the same functions should be added to the CommandBuffer interface (or similar name) to be used in a different situation.

Since the modern APIs require to explicitly submit command buffers, buffer updates do not become visible in the same order as the drawing commands are encoded. Consider the following example:

// Render model M with transformation matrix X
renderer->WriteBuffer(*constantBuffer, ...);
cmdBuffer->Draw(...);

// Render model M with transformation matrix Y
renderer->WriteBuffer(*constantBuffer, ...);
cmdBuffer->Draw(...);

This won't work with Vulkan for instance, since the content of constantBuffer is modified multiple times during the command encoding but the commands have not yet been submitted to the GPU.

This, however, can be achieved by writing the content, the buffer is to be updated with, directly into the command buffer (e.g. with vkCmdUpdateBuffer for Vulkan). Therefore, the WriteBuffer function should also be part of the CommandBuffer interface to update constant buffers during rendering.

The old WriteBuffer function in the RenderSystem interface can then only be used outside of command encoding and submitting command buffers must be scheduled properly by the client programmer.

Proposal:

// Update large buffers
renderer->WriteBuffer(*largeStorageBuffer, ...);

// Render
cmdBuffer->Begin();
{
    // Render model M with transformation X
    cmdBuffer->WriteBuffer(*smallConstantBuffer, ...);
    cmdBuffer->BeginRenderPass(...);
    {
        cmdBuffer->Draw(...);
    }
    cmdBuffer->EndRenderPass();
    
    // Render model M with transformation Y
    cmdBuffer->WriteBuffer(*smallConstantBuffer, ...);
    cmdBuffer->BeginRenderPass(...);
    {
        cmdBuffer->Draw(...);
    }
    cmdBuffer->EndRenderPass();
}
cmdBuffer->End();
cmdQueue->Submit(*cmdBuffer);
context->Present();

Caveat:

  • vkCmdUpdateBuffer can only appear outside of a render pass.
  • vkCmdUpdateBuffer can only update buffers with a maximum size of 65536 bytes (i.e. primarily useful for constant buffers only).
  • Starting a new render pass every time a constant buffer must be updated is very unpleasant, but unfortunately the only way as it looks right now :(

What's the minimum OpenGL version supported?

I couldn't find any statement on the minimum version of OpenGL that LLGL is written for. While working with the sample Cpp programs I noticed that LLGL always enables GL_TEXTURE_CUBE_MAP_SEAMLESS which is a 3.2 feature and GL_PRIMITIVE_RESTART_FIXED_INDEX which is a 4.3 feature. On earlier versions either of these calls could cause an INVALID_ENUM error to be logged.

Since the OpenGL on the Mac is frozen at version 4.1 GL_PRIMITIVE_RESTART_FIXED_INDEX doesn't work. I'm trying to re-implement this using GL_PRIMITIVE_RESTART but that requires a minimum version of 3.1. Can 3.1 features be invoked unconditionally?

Multithreading example crash using DX12 renderer

When running the Multi-Threading example (in debug mode) using the DirectX12 renderer I got the following error:

D3D12 ERROR: ID3D12CommandQueue::ExecuteCommandLists: A command list, which writes to a swapchain back buffer, may only be executed when that back buffer is the back buffer that will be presented during the next call to Present*. Such a back buffer is also referred to as the "current back buffer". Swap Chain: 0x0000022962A35080:'Unnamed Object' - Current Back Buffer Buffer: 0x0000022961DA41B0:'Unnamed ID3D12Resource Object' - Attempted Write Buffer: 0x0000022961DA6880:'Unnamed ID3D12Resource Object' [ STATE_SETTING ERROR #907: EXECUTECOMMANDLISTS_WRONGSWAPCHAINBUFFERREFERENCE]
D3D12: Removing Device.
D3D12 ERROR: ID3D12Device::RemoveDevice: Device removal has been triggered for the following reason (DXGI_ERROR_ACCESS_DENIED: The application attempted to use a resource it does not access to.  This  could be, for example, rendering to a texture while only having read access.). [ EXECUTION ERROR #232: DEVICE_REMOVAL_PROCESS_AT_FAULT]

Console output:

selected renderer: Direct3D12
render system:
  renderer:           Direct3D 11.1
  device:             NVIDIA GeForce RTX 2060 SUPER
  vendor:             NVIDIA Corporation
  shading language:   HLSL 6.0

render context:
  resolution:         800 x 600
  samples:            8
  colorFormat:        RGBA8UNorm
  depthStencilFormat: D24UNormS8UInt

Enter thread: mainThread
Leave thread: mainThread
failed to present DXGI swap chain (error code = DXGI_ERROR_DEVICE_REMOVED)

Query result buffer support

D3D12 always requires an explicit buffer resource for the results of a query heap.
OpenGL added a buffer type GL_QUERY_BUFFER so the query results can be used in a shader.
This functionality should be added to LLGL.

Proposal:

enum class BufferType {
    /* ... */
    QueryResult,
};

struct BufferFlags {
    enum {
        /* ... */
        // Force creation of query buffer also for GL backend.
        // Otherwise, the buffer is only created for D3D12.
        QueryShaderReadAccess = (1 << 3),
    };
};

class CommandBuffer {
    /* ... */
    void ResolveQuery(
        Buffer&         dstBuffer,
        std::uint64_t   dstOffset,
        QueryHeap&      srcQueryHeap,
        std::uint32_t   firstQuery,
        std::uint32_t   numQueries
    );
};

Example:

myCmdBuffer->Begin();
myCmdBuffer->BeginRenderPass();
myCmdBuffer->BeginQuery(*myQuery, 0);
/* ... */
myCmdBuffer->EndQuery(*myQuery, 0);
myCmdBuffer->EndRenderPass();
myCmdBuffer->ResolveQuery(*myResultBuffer, 0, *myQuery, 0, 1);
myCmdBuffer->End();

myCmdQueue->Submit(*myCmdBuffer);

std::uint64_t result = 0;
if (myCmdQueue->QueryResult(*myQuery, 0, 1, &result, sizeof(result))) {
    /* ... */
}

Can't run the examples

Hi, I'm trying to run the examples, but I can't get it to work.

I followed the setup instructions to install everything and it all builds fine.
(I'm using visual studio 2019)

When I run the develop build for the PBR example and pick the DirectX12 option in the console I get a
"failed to open file: Example.hlsl" error

I then manually setup the Debugging->Working Directory to point to the project directory, and that fixed the loading issue.

After that I get a crash on D3D12CommandContext::SetPipelineState

traced it down to:

D3D12MipGenerator::CreateComputePSO
if (auto blob = DXCreateBlobFromResource(resourceID)) returning null

/* Load resource from binary data (*.rc file) */
if (HRSRC resource = FindResource(moduleHandle, MAKEINTRESOURCE(resourceID), RT_RCDATA))

I don't seem to have this .rc file. Did I miss something?

how to debug low level api ?

i want to debug low level api while using clion, however , i found the target version of render system is loaded dynamically using dlopen , its possible to debug the OPENGL api ?
2019-03-05 11 23 31

Remove "ShaderProgram" interface

Replace the ShaderProgram interface and use Shader instances individually in various combinations for a GraphicsPipeline:

Before:

LLGL::ShaderProgramDescriptor myProgramDesc;
myProgramDesc.vertexShader = myVertShader;
myProgramDesc.fragmentShader = myFragShader;
LLGL::ShaderProgram* myShaderProgram = myRenderer->CreateShaderProgram(myProgramDesc);

LLGL::GraphicsPipelineDescriptor myPipelineDesc;
myPipelineDesc.shaderProgram = myShaderProgram;
/* ... */

After:

LLGL::GraphicsPipelineDescriptor myPipelineDesc;
myPipelineDesc.vertexShader = myVertShader;
myPipelineDesc.fragmentShader = myFragShader;
/* ... */

For OpenGL there need to be two paths implemented:

  1. Using GL_ARB_separate_shader_objects extension.
  2. Using an internal pool to look up existing shader programs that already exist for a combination of the specified shaders, i.e. the GLShaderProgram implementation will remain as internal class as fallback when the extension (see 1.) is not available.

All the shader reflection will move into Shader interface, which might be a bit chellenging for the 2nd case for OpenGL, because all shader related glGet* function need a shader program, i.e. a dummy shader program needs to be created, e.g. with a dummy vertex or fragment shader.

RenderSystem, RenderContext, VideoMode refactor

RenderSystem interface should be renamed to Device and RenderContext should be renamed to SwapChain.
Along this renaming, the RenderContextDescriptor, VsyncDescriptor and VideoModeDescriptor should be merged into SwapChainDescriptor like this:

Before:

struct VsyncDescriptor {
    bool            enabled     = false;
    std::uint32_t   refreshRate = 60;
    std::uint32_t   interval    = 1;
};

struct VideoModeDescriptor {
    Extent2D        resolution;
    int             colorBits       = 32;
    int             depthBits       = 24;
    int             stencilBits     = 8;
    bool            fullscreen      = false;
    std::uint32_t   swapChainSize   = 2;
};

struct RenderContextDescriptor {
    VsyncDescriptor         vsync;
    std::uint32_t           samples     = 1;
    VideoModeDescriptor     videoMode;
};

After:

struct SwapChainDescriptor {
    Extent2D        resolution;
    int             colorBits       = 32;
    int             depthBits       = 24;
    int             stencilBits     = 8;
    bool            fullscreen      = false;
    std::uint32_t   swapBuffers     = 2;
//  std::uint32_t   refreshRate     = 60; // Refresh rate is display dependent and should not be an option here
    std::uint32_t   samples         = 1;
    std::uint32_t   vsyncInterval   = 0;
};

Great library and some questions.

Hi, recently I have been using LLGL to replace the ugly underlying API layer in my engine, I am a big fan of LLGL architecture as I hate opengl-like architecture "ex: bgfx", anyways I have some questions about this library.

1-Why supporting OpenGL?
Vulkan is a great successor and run on almost all platforms that OpenGL run on, and can run on mobile devices plus it has DirectX-like structure and a modern API. The reason I dislike OpenGL is it has some sort of legacy function that is still used until now! for example it requires a vertex format for each vertex buffer created!
correct me if I am wrong but it is the only API that does that in 2018.

2-Do I have to create a resource heap for each pair of Texture and sampler in every shader?

3-Will I need more than one CommandBuffer for my project?

4-Why is "RenderSystem" / "Renderer" named with these names? shouldn't they be named Context and the "Context" class should be named Device?

  • I kinda prefer DirectX namings + naming anything with "Renderer" can cause huge confusion especially with newcomers.

Create todo list

Hi.
Thanks for for sharing this beautiful library with us. I would be glad to help this project with contributions. But I don't know which parts aren't implemented or need maintenance.

In readme file, project progress described with percentage. For example ~30% of Vulkan renderer is complete. Unfortunately percentage is not enough for new contributors. Isn't it better to create todo list of which parts are not done?

Good news is that github already has that feature in Projects tab.

Dx11 bug with caps.features.hasLogicOp.

There is an inconsistency between this lines:
DXCode.cpp:
caps.features.hasLogicOp = (featureLevel >= D3D_FEATURE_LEVEL_11_1);
DX11Types.cpp (function Convert):
if (src.logicOp != LogicOp::Disabled)
without define LLGL_D3D11_ENABLE_FEATURELEVEL added to compile.

Value of hasLogicOp can be set to true by D3D_FEATURE_LEVEL_11_1 but the code assumes it's disabled without LLGL_D3D11_ENABLE_FEATURELEVEL

Possible Error/Mistake in LinuxWindow

The event handler is trying to catch 'DestroyNotify'. Without having the code tested on an actual linux machine, there's probably a mistake.

  • The event mask must specify "SubstructureNotifyMask".
  • By specification, this event is only triggered, when the window is being destroyed by either "XDestroyWindow" or "XDestroySubwindows".

I am not sure whether this last point should belong to the former one:

  • Closing the window, by pressing the "X"-Button in the decoration is not being handled. The X11 protocol does not define such an event explicitely, but by using Extensions.

See here

Community discussion

One thing I've found extremely useful while working with various Open Source projects is the ability to have a community to talk to, including the very same authors.

LLGL at the current state does not offer any kind of Discussion channel around this project. My proposal would be to either consider one of these following options:

  • IRC
  • Gitter
  • Discord

IRC

Although it's very used and popular, most of the people today are no more keen to download, install or use IRC in general. I would personally NOT suggest this option.

Gitter

Although this platform is very popular among Developers, it's not really the best platform to chat into. Lack of rooms, is definitely a CONs. One of the biggest PROs is definitely the fact that to start chatting most probably the users already do have one of the required accounts to login.

Discord

Pretty much used by gamers, but not only to. For eg. Microsoft is one of those having a Discord channel supporting also Developers. The fact you can create rooms and have also vocal rooms makes this definitely a very good platform to get in touch, also with non-written communication.


I would like to use this issue to solve and ideally choose the right platform in order to get in touch. I already have some questions regarding LLGL and Shader loading, but I fear I would fill the issue list a lot, and maybe not all the users may find it useful. As well as you being distracted by "managing issues" than actually focusing on the real problems.

Thank you in advance if you ever consider this!

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.