Skip to content

Commit

Permalink
Refactored barrier flags interface.
Browse files Browse the repository at this point in the history
- Barrier flags have been moved from ResourceHeapDescriptor to PipelineLayoutDescriptor.
- This allows memory barriers to be applied for individual resources, not just the ResourceHeap.
- Extended Parse() syntax to support barrier flags in pipeline layouts, e.g. "barriers{rw}".
- GL backend must flush invalidated memory barriers before every draw/dispatch call.
- Deprecated ResourceHeapDescriptor::barrierFlags field.
- Added missing "debugName" field to C99 wrapper conversion of pipeline layouts.
- Updated ClothPhysics example to use new API.
- Updated D3D12, GL, Vulkan backends for new API (others don't care about barriers).
  • Loading branch information
LukasBanana committed May 12, 2024
1 parent e20f1fa commit df53521
Show file tree
Hide file tree
Showing 35 changed files with 380 additions and 119 deletions.
7 changes: 7 additions & 0 deletions docu/ChangeLog/ChangeLog-v0.04.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Version 0.04 introduced deprecated attributes (via `LLGL_DEPRECATED`) to keep co
- [Rendering features](#rendering-features)
- [`FrameProfile` structure](#frameprofile-structure)
- [Binding model](#binding-model)
- [Resource barriers](#resource-barriers)
- [Renamed identifiers](#renamed-identifiers)


Expand Down Expand Up @@ -82,6 +83,12 @@ Rationale:
3. Modern rendering APIs (i.e. Vulkan, D3D12, Metal) either use a resource heap binding model or command encoder that don't save the binding state across multiple frames (or across multiple render-passes). Since LLGL aims to be aligned towards the newer APIs, the older backends should emulate that same functionality and not require to manually unbind resources.


## Resource barriers

Alongside a new binding model, the resource barriers are no longer specified per `ResourceHeap` but per `PipelineLayout` to make them accessible to individual bindings as well.
Therefore, `ResourceHeapDescriptor::barrierFlags` has been deprecated and superseded by `PipelineLayoutDescriptor::barrierFlags`.


## Renamed identifiers

The following identifiers have also been renamed and their old names have been deprecated as type aliases, i.e. they are still available but will be removed in the next version of LLGL:
Expand Down
19 changes: 8 additions & 11 deletions examples/Cpp/ClothPhysics/Example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -387,12 +387,13 @@ class Example_ClothPhysics : public ExampleBase
"cbuffer(SceneState@0):comp,"
#ifdef ENABLE_STORAGE_TEXTURES
"texture(parBase@1):comp,"
"rwtexture(parCurrPos@2, parNextPos@3, parPrevPos@4, parVelocity@5, parNormal@6):comp"
"rwtexture(parCurrPos@2, parNextPos@3, parPrevPos@4, parVelocity@5, parNormal@6):comp,"
#else
"buffer(parBase@1):comp,"
"rwbuffer(parCurrPos@2, parNextPos@3, parPrevPos@4, parVelocity@5, parNormal@6):comp"
"}"
"rwbuffer(parCurrPos@2, parNextPos@3, parPrevPos@4, parVelocity@5, parNormal@6):comp,"
"},"
#endif // /ENABLE_STORAGE_TEXTURES
"barriers{rwbuffer},"
)
);

Expand All @@ -419,7 +420,6 @@ class Example_ClothPhysics : public ExampleBase
{
resourceHeapDesc.pipelineLayout = computeLayout;
resourceHeapDesc.numResourceViews = sizeof(resourceViewsCompute) / sizeof(resourceViewsCompute[0]);
resourceHeapDesc.barrierFlags = LLGL::BarrierFlags::StorageBuffer;
}
computeResourceHeap = renderer->CreateResourceHeap(resourceHeapDesc, resourceViewsCompute);

Expand Down Expand Up @@ -471,16 +471,16 @@ class Example_ClothPhysics : public ExampleBase

graphicsLayout = renderer->CreatePipelineLayout(
IsMetal() || IsVulkan()
? LLGL::Parse("heap{cbuffer(SceneState@3):vert:frag, texture(colorMap@4):frag, sampler(linearSampler@5):frag, texture(1,2,6):vert}")
: LLGL::Parse("heap{cbuffer(SceneState@0):vert:frag, texture(colorMap@0):frag, sampler(linearSampler@0):frag, texture(1,2,3):vert}")
? LLGL::Parse("heap{cbuffer(SceneState@3):vert:frag, texture(colorMap@4):frag, sampler(linearSampler@5):frag, texture(1,2,6):vert}, barriers{rwtexture}")
: LLGL::Parse("heap{cbuffer(SceneState@0):vert:frag, texture(colorMap@0):frag, sampler(linearSampler@0):frag, texture(1,2,3):vert}, barriers{rwtexture}")
);

#else

graphicsLayout = renderer->CreatePipelineLayout(
IsMetal() || IsVulkan()
? LLGL::Parse("heap{cbuffer(SceneState@3):vert:frag, texture(colorMap@4):frag, sampler(linearSampler@5):frag}")
: LLGL::Parse("heap{cbuffer(SceneState@0):vert:frag, texture(colorMap@0):frag, sampler(linearSampler@0):frag}")
? LLGL::Parse("heap{cbuffer(SceneState@3):vert:frag, texture(colorMap@4):frag, sampler(linearSampler@5):frag},")
: LLGL::Parse("heap{cbuffer(SceneState@0):vert:frag, texture(colorMap@0):frag, sampler(linearSampler@0):frag},")
);

#endif // /ENABLE_STORAGE_TEXTURES
Expand Down Expand Up @@ -518,9 +518,6 @@ class Example_ClothPhysics : public ExampleBase
{
resourceHeapDesc.pipelineLayout = graphicsLayout;
resourceHeapDesc.numResourceViews = sizeof(resourceViewsGraphics) / sizeof(resourceViewsGraphics[0]);
#ifdef ENABLE_STORAGE_TEXTURES
resourceHeapDesc.barrierFlags = LLGL::BarrierFlags::StorageTexture;
#endif
}
graphicsResourceHeap = renderer->CreateResourceHeap(resourceHeapDesc, resourceViewsGraphics);
}
Expand Down
19 changes: 10 additions & 9 deletions include/LLGL-C/LLGLWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,14 @@ typedef enum LLGLFormatFlags
}
LLGLFormatFlags;

typedef enum LLGLBarrierFlags
{
LLGLBarrierStorageBuffer = (1 << 0),
LLGLBarrierStorageTexture = (1 << 1),
LLGLBarrierStorage = (LLGLBarrierStorageBuffer | LLGLBarrierStorageTexture),
}
LLGLBarrierFlags;

typedef enum LLGLColorMaskFlags
{
LLGLColorMaskZero = 0,
Expand Down Expand Up @@ -883,14 +891,6 @@ typedef enum LLGLMiscFlags
}
LLGLMiscFlags;

typedef enum LLGLBarrierFlags
{
LLGLBarrierStorageBuffer = (1 << 0),
LLGLBarrierStorageTexture = (1 << 1),
LLGLBarrierStorage = (LLGLBarrierStorageBuffer | LLGLBarrierStorageTexture),
}
LLGLBarrierFlags;

typedef enum LLGLShaderCompileFlags
{
LLGLShaderCompileDebug = (1 << 0),
Expand Down Expand Up @@ -1189,7 +1189,7 @@ typedef struct LLGLResourceHeapDescriptor
const char* debugName; /* = NULL */
LLGLPipelineLayout pipelineLayout; /* = LLGL_NULL_OBJECT */
uint32_t numResourceViews; /* = 0 */
long barrierFlags; /* = 0 */
long barrierFlags; /* ResourceHeapDescriptor.barrierFlags is deprecated since 0.04b; Use PipelineLayoutDescriptor.barrierFlags instead! */
}
LLGLResourceHeapDescriptor;

Expand Down Expand Up @@ -1667,6 +1667,7 @@ typedef struct LLGLPipelineLayoutDescriptor
const LLGLStaticSamplerDescriptor* staticSamplers; /* = NULL */
size_t numUniforms; /* = 0 */
const LLGLUniformDescriptor* uniforms; /* = NULL */
long barrierFlags; /* = 0 */
}
LLGLPipelineLayoutDescriptor;

Expand Down
2 changes: 1 addition & 1 deletion include/LLGL/CommandBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ class LLGL_EXPORT CommandBuffer : public RenderSystemChild
*/
virtual void SetResource(std::uint32_t descriptor, Resource& resource) = 0;

//! \deprecated
//! \deprecated Since 0.04b; No need to reset resource slots manually anymore!
LLGL_DEPRECATED("CommandBuffer::ResetResourceSlots is deprecated since 0.04b; No need to reset resource slots manually anymore!")
virtual void ResetResourceSlots(
const ResourceType resourceType,
Expand Down
45 changes: 45 additions & 0 deletions include/LLGL/PipelineLayoutFlags.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,43 @@ namespace LLGL
{


/* ----- Flags ----- */

/**
\brief Flags for memory barriers in pipeline layouts.
\see PipelineLayoutDescriptor::barrierFlags
*/
struct BarrierFlags
{
enum
{
/**
\brief Memory barrier for Buffer resources that were created with the BindFlags::Storage bind flags.
\remarks Shader access to the buffer will reflect all data written to by previous shaders.
\see BindFlags::Storage
*/
StorageBuffer = (1 << 0),

/**
\brief Memory barrier for Texture resources that were created with the BindFlags::Storage bind flags.
\remarks Shader access to the texture will reflect all data written to by previous shaders.
\see BindFlags::Storage
*/
StorageTexture = (1 << 1),

/**
\brief Memory barrier for any storage resource. This is just a bitwise OR combination of \c StorageBuffer and \c StorageTexture.
\remarks Renderer backends such as Direct3D 12 and Vulkan have bookkeeping for storage resources
and don't have to distinguish between Buffer and Texture resource views for their barriers at time of creating the ResourceHeap.
Hence, using BarrierFlags::Storage by default when any resource views in the ResourceHeap have to be synchronized is recommended.
Only the OpenGL backend has to know at creation time what type of resources need a global barrier via \c glMemoryBarrier.
\see BindFlags::Storage
*/
Storage = (StorageBuffer | StorageTexture),
};
};


/* ----- Enumerations ----- */

/**
Expand Down Expand Up @@ -375,6 +412,14 @@ struct PipelineLayoutDescriptor
\see CommandBuffer::SetUniforms
*/
std::vector<UniformDescriptor> uniforms;

/**
\brief Specifies optional resource barrier flags. By default 0.
\remarks If the barrier flags are non-zero, they will be applied before any resource are bound to the graphics/compute pipeline.
This should be used when a resource is bound to the pipeline that was previously written to.
\see BarrierFlags
*/
long barrierFlags = 0;
};


Expand Down
60 changes: 16 additions & 44 deletions include/LLGL/ResourceHeapFlags.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <LLGL/TextureFlags.h>
#include <LLGL/Buffer.h>
#include <LLGL/BufferFlags.h>
#include <LLGL/Deprecated.h>
#include <vector>


Expand All @@ -24,43 +25,6 @@ namespace LLGL
class PipelineLayout;


/* ----- Flags ----- */

/**
\brief Flags for memory barriers in resource heaps.
\see ResourceHeapDescriptor::barrierFlags
*/
struct BarrierFlags
{
enum
{
/**
\brief Memory barrier for Buffer resources that were created with the BindFlags::Storage bind flags.
\remarks Shader access to the buffer will reflect all data written to by previous shaders.
\see BindFlags::Storage
*/
StorageBuffer = (1 << 0),

/**
\brief Memory barrier for Texture resources that were created with the BindFlags::Storage bind flags.
\remarks Shader access to the texture will reflect all data written to by previous shaders.
\see BindFlags::Storage
*/
StorageTexture = (1 << 1),

/**
\brief Memory barrier for any storage resource. This is just a bitwise OR combination of \c StorageBuffer and \c StorageTexture.
\remarks Renderer backends such as Direct3D 12 and Vulkan have bookkeeping for storage resources
and don't have to distinguish between Buffer and Texture resource views for their barriers at time of creating the ResourceHeap.
Hence, using BarrierFlags::Storage by default when any resource views in the ResourceHeap have to be synchronized is recommended.
Only the OpenGL backend has to know at creation time what type of resources need a global barrier via \c glMemoryBarrier.
\see BindFlags::Storage
*/
Storage = (StorageBuffer | StorageTexture),
};
};


/* ----- Structures ----- */

/**
Expand Down Expand Up @@ -145,14 +109,26 @@ struct ResourceHeapDescriptor
{
ResourceHeapDescriptor() = default;

LLGL_DEPRECATED_IGNORE_PUSH()

//! Initializes the resource heap descriptor with the specified pipeline layout and optional secondary parameters.
inline ResourceHeapDescriptor(PipelineLayout* pipelineLayout, std::uint32_t numResourceViews = 0, long barrierFlags = 0) :
inline ResourceHeapDescriptor(PipelineLayout* pipelineLayout, std::uint32_t numResourceViews = 0) :
pipelineLayout { pipelineLayout },
numResourceViews { numResourceViews }
{
}

//! \deprecated Since 0.04b; Use PipelineLayoutDescriptor::barrierFlags instead!
LLGL_DEPRECATED("ResourceHeapDescriptor::barrierFlags is deprecated since 0.04b; Use PipelineLayoutDescriptor::barrierFlags instead!")
inline ResourceHeapDescriptor(PipelineLayout* pipelineLayout, std::uint32_t numResourceViews, long barrierFlags) :
pipelineLayout { pipelineLayout },
numResourceViews { numResourceViews },
barrierFlags { barrierFlags }
{
}

LLGL_DEPRECATED_IGNORE_POP()

/**
\brief Optional name for debugging purposes. By default null.
\remarks The final name of the native hardware resource is implementation defined.
Expand All @@ -173,12 +149,8 @@ struct ResourceHeapDescriptor
*/
std::uint32_t numResourceViews = 0;

/**
\brief Specifies optional resource barrier flags. By default 0.
\remarks If the barrier flags are non-zero, they will be applied before any resource are bound to the graphics/compute pipeline.
This should be used when a resource is bound to the pipeline that was previously written to.
\see BarrierFlags
*/
//! \deprecated Since 0.04b; Use PipelineLayoutDescriptor::barrierFlags instead!
LLGL_DEPRECATED("ResourceHeapDescriptor::barrierFlags is deprecated since 0.04b; Use PipelineLayoutDescriptor::barrierFlags instead!")
long barrierFlags = 0;
};

Expand Down
8 changes: 7 additions & 1 deletion include/LLGL/Utils/Parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,19 @@ class LLGL_EXPORT ParseContext
- \c frag for the fragment shader stage (i.e. StageFlags::FragmentStage).
- \c comp for the compute shader stage (i.e. StageFlags::ComputeStage).
- If no stage flag is specified, all shader stages will be used.
- There is a secondary syntax for uniform descriptors (see LLGL::UniformType for accepted type names):
- The following syntax can be used for uniform descriptors (see LLGL::UniformType for accepted type names):
\code
arraySize := '[' INT ']'
uniform := NAME | NAME arraySize
uniformList := uniform | uniform ',' uniformList
uniformDesc := TYPE '(' uniformList ')'
\endcode
- The following syntax can be used for barrier flags (see PipelineLayoutDescriptor::barrierFlags):
\code
barriers := 'barriers' '{' flags '}'
flags := FLAG | FLAG ',' | FLAG ',' flags
FLAG := 'rw' | 'rwbuffer' | 'rwtexture'
\endcode
- Whitespaces are ignored (e.g. blanks <code>' '</code>, tabulators <code>'\\t'</code>, new-line characters <code>'\\n'</code> and <code>'\\r'</code> etc.), see C++ STL function <code>std::isspace</code>.
\remarks Here is a usage example:
\code
Expand Down
56 changes: 56 additions & 0 deletions sources/Core/Parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -731,12 +731,38 @@ static UniformType StringToUniformType(StringView s)
return UniformType::Undefined;
}

static bool ParseLayoutSignatureBarrierFlag(Parser& parser, PipelineLayoutDescriptor& outDesc)
{
if (parser.Match("rw"))
{
parser.Accept();
outDesc.barrierFlags |= BarrierFlags::Storage;
return true;
}
else if (parser.Match("rwbuffer"))
{
parser.Accept();
outDesc.barrierFlags |= BarrierFlags::StorageBuffer;
return true;
}
else if (parser.Match("rwtexture"))
{
parser.Accept();
outDesc.barrierFlags |= BarrierFlags::StorageTexture;
return true;
}
return ReturnWithParseError(parser, "unknown barrier flag: %s", parser.Token());
}

static bool ParseLayoutSignatureBinding(Parser& parser, PipelineLayoutDescriptor& outDesc, bool isHeap)
{
/* Check if resource type denotes a uniform binding */
const UniformType uniformType = StringToUniformType(parser.Token());
if (uniformType != UniformType::Undefined)
{
if (isHeap)
return ReturnWithParseError(parser, "uniform bindings must not be declared inside a heap");

parser.Accept();

if (!parser.Accept("("))
Expand Down Expand Up @@ -778,6 +804,36 @@ static bool ParseLayoutSignatureBinding(Parser& parser, PipelineLayoutDescriptor
return true;
}

/* Check if resource type denotes a barrier bitmask */
if (parser.Match("barriers"))
{
if (isHeap)
return ReturnWithParseError(parser, "barrier flags must not be declared inside a heap");

parser.Accept();

if (!parser.Accept("{"))
return ReturnWithParseError(parser, "expected open curly bracket '{' after barrier flags");

while (parser.Feed() && !parser.Match("}"))
{
/* Parse next barrier flag */
if (!ParseLayoutSignatureBarrierFlag(parser, outDesc))
return false;

/* If there's no comma, the flags block must end */
if (parser.Match(","))
parser.Accept();
else
break;
}

if (!parser.Accept("}"))
return ReturnWithParseError(parser, "expected closing curly bracket '}' after end of barrier flags");

return true;
}

/* Otherwise, parse resource binding */
return ParseLayoutSignatureResourceBinding(parser, outDesc, isHeap);
}
Expand Down
Loading

0 comments on commit df53521

Please sign in to comment.