Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Load/Store Local/Shared and Atomic shared using new instructions #5241

Merged
merged 7 commits into from Jun 15, 2023

Conversation

gdkchan
Copy link
Member

@gdkchan gdkchan commented Jun 5, 2023

This is the last PR in the series of PRs to migrate memory related shader operations to the new Load and Store IR instructions that also carry a StorageKind, which I implemented a while ago. This one does the local and shared memory types, which are the only ones remaining. Those are not really buffer backed, rather they use some private memory. Shared memory is only valid for compute shaders, and is shared by all invocations on the workgroup, but is not externally visible. Meanwhile, local memory is only visible within a single invocation,

There are 2 helper functions, one for storing small integer (8-bit and 16-bit) values on shared memory, and another for doing atomic operations on signed integers. The latter is only needed on GLSL since SPIR-V has separate instructions for signed and unsigned atomic operations, regardless of the variable type. Both were moved out of the backend and are now generated at the IR level, like what I did on the previous changes.

Unlike the old implementation, this one allows multiple local memories and shared memories to exist. The old one only allowed one. This can be useful if we combine shader stages in the future for example. It is also good for symmetry: We have the first operand of all Load and Store operations reserved for the buffer binding number, which must be constant. For local and shared memory, they represent an ID that identifies the memory used.

Advantages are the same of the previous PRs: More code sharing between GLSL and SPIR-V, makes new backends easier to implement, and simpler code (this code manages to remove almost 100 lines of code which is quite nice). It also makes the IR more flexible as we can have multiple separate memory storages now if we need them for different purposes.

This also fixes a regression from #4993, but not a user facing one: The set index value was not being set when generating GLSL code, which made GLSL shaders to not work properly on Vulkan. Currently there's no way for users to enable that, it must be enabled manually by changing the code.

After that, there's still work to be done on the shader refactoring:

  • Move texture bindings information to ShaderProperties like buffers and memory.
  • Move remaining values to TranslatorContext and delete ShaderConfig.
  • Move instruction type to Operation, stop depending on InstructionInfo on GLSL generation and delete InstructionInfo.
  • Ensure all remaining texture and packing operations can use vector outputs and delete Index field from Operation.
  • Stop using IGpuAccessor on the backends and store all required information on ShaderProperties.

Testing is welcome.

@gdkchan gdkchan added enhancement New feature or request gpu Related to Ryujinx.Graphics labels Jun 5, 2023
Copy link
Contributor

@patrick-hovsepian patrick-hovsepian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor comments/observations

@@ -68,7 +68,7 @@ private static string GetExpression(CodeGenContext context, AstOperation operati

string args = string.Empty;

if (atomic && operation.StorageKind == StorageKind.StorageBuffer)
if (atomic && (operation.StorageKind == StorageKind.StorageBuffer || operation.StorageKind == StorageKind.SharedMemory))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super nit: might be more readable with is and or if it works in this context

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of is, or, and etc expressions. It is easier to understand like this for me.

Comment on lines +141 to +148
if (!(operation.GetSource(0) is AstOperand bindingId) || bindingId.Type != OperandType.Constant)
{
throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand.");
}

MemoryDefinition memory = operation.StorageKind == StorageKind.LocalMemory
? context.Config.Properties.LocalMemories[bindingId.Value]
: context.Config.Properties.SharedMemories[bindingId.Value];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

worth making a helper function that takes a source index and returns a memory definition so it can be reused here and in InstGenMemory?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I plan to delete that code soon-ish, so I didn't worry too much about de-duplicating it.

src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs Outdated Show resolved Hide resolved
src/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs Outdated Show resolved Hide resolved
Comment on lines +7 to +9
public string Name { get; }
public AggregateType Type { get; }
public int ArrayLength { get; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

worth marking these as init?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might imply the constructor too and you could default ArrayLength to 1

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the benefit of using init here? AFAIK with init the property can only be set on the constructor, but get-only properties also behave like this already, they can only be initialized on the constructor.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my bad, force of habit and I'm partial to object initializer syntax. I also thought by making it a record the compiler would generate the constructor for you


Operand result = GenerateSharedAtomicCasLoop(context, wordOffset, id, (memValue) =>
{
return isMin
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

worth making this a local function? There's an implication with the closures but I don't recall offhand

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could extract it into a separate function. Both are fine for me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine. I feel like @marco-carvalho might be interested in benchmarking this vs a local function?


namespace Ryujinx.Graphics.Shader.StructuredIr
{
readonly struct MemoryDefinition
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

worth making it a record to get pass by reference semantics?

@Exhigh
Copy link
Contributor

Exhigh commented Jun 12, 2023

Needs a rebase

@github-actions github-actions bot removed the gpu Related to Ryujinx.Graphics label Jun 12, 2023
Copy link
Member

@riperiperi riperiperi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good, sorry for the late review.

@gdkchan gdkchan merged commit f92921a into Ryujinx:master Jun 15, 2023
9 checks passed
@gdkchan gdkchan deleted the lsmem-v2 branch June 15, 2023 20:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants