From 8819d657211679162b3eaf8123a023b3e2cae94c Mon Sep 17 00:00:00 2001 From: Johan Gustafsson Date: Fri, 21 Mar 2025 11:26:05 +0100 Subject: [PATCH 1/7] feat: Added compute shader support for vulkan Vulkan graphics backend has been modified to support compute shaders, additional modifications were also made to the shader compiler so that correct glsl compute shaders can be generated. --- .../Vulkan/CommandList.Vulkan.cs | 44 ++- .../Vulkan/GraphicsDevice.Vulkan.cs | 17 +- .../Vulkan/PipelineState.Vulkan.cs | 273 ++++++++++-------- .../Vulkan/VulkanConvertExtensions.cs | 11 + .../ColorTransforms/ColorTransformGroup.cs | 7 +- .../Rendering/Images/PostProcessingEffects.cs | 3 + .../OpenGL/ShaderCompiler.cs | 8 + .../Mixins/StrideShaderMixer.cs | 15 +- .../Ast/StorageQualifier.cs | 20 ++ .../Convertor/GlobalUniformVisitor.cs | 10 +- .../Convertor/HlslToGlslConvertor.cs | 248 +++++++++++----- .../Convertor/HlslToGlslWriter.cs | 27 +- 12 files changed, 458 insertions(+), 225 deletions(-) diff --git a/sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs b/sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs index 0830f13bd9..f2784c4040 100644 --- a/sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs +++ b/sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs @@ -130,9 +130,9 @@ private unsafe void FlushInternal(bool wait) if (activePipeline != null) { - vkCmdBindPipeline(currentCommandList.NativeCommandBuffer, VkPipelineBindPoint.Graphics, activePipeline.NativePipeline); + vkCmdBindPipeline(currentCommandList.NativeCommandBuffer, activePipeline.IsCompute ? VkPipelineBindPoint.Compute : VkPipelineBindPoint.Graphics, activePipeline.NativePipeline); var descriptorSetCopy = descriptorSet; - vkCmdBindDescriptorSets(currentCommandList.NativeCommandBuffer, VkPipelineBindPoint.Graphics, activePipeline.NativeLayout, firstSet: 0, descriptorSetCount: 1, &descriptorSetCopy, dynamicOffsetCount: 0, dynamicOffsets: null); + vkCmdBindDescriptorSets(currentCommandList.NativeCommandBuffer, activePipeline.IsCompute ? VkPipelineBindPoint.Compute : VkPipelineBindPoint.Graphics, activePipeline.NativeLayout, firstSet: 0, descriptorSetCount: 1, &descriptorSetCopy, dynamicOffsetCount: 0, dynamicOffsets: null); } SetRenderTargetsImpl(depthStencilBuffer, renderTargetCount, renderTargets); } @@ -249,7 +249,11 @@ private unsafe void PrepareDraw() // Lazily set the render pass and frame buffer EnsureRenderPass(); + BindDescriptorSets(); + } + private unsafe void BindDescriptorSets() + { // Keep track of descriptor pool usage bool isPoolExhausted = ++allocatedSetCount > GraphicsDevice.MaxDescriptorSetCount; for (int i = 0; i < DescriptorSetLayout.DescriptorTypeCount; i++) @@ -328,7 +332,7 @@ private unsafe void PrepareDraw() sType = VkStructureType.WriteDescriptorSet, descriptorType = mapping.DescriptorType, dstSet = localDescriptorSet, - dstBinding = (uint) mapping.DestinationBinding, + dstBinding = (uint)mapping.DestinationBinding, dstArrayElement = 0, descriptorCount = 1 }; @@ -336,10 +340,20 @@ private unsafe void PrepareDraw() switch (mapping.DescriptorType) { case VkDescriptorType.SampledImage: - var texture = heapObject.Value as Texture; - descriptorData->ImageInfo = new VkDescriptorImageInfo { imageView = texture?.NativeImageView ?? GraphicsDevice.EmptyTexture.NativeImageView, imageLayout = VkImageLayout.ShaderReadOnlyOptimal }; - write->pImageInfo = &descriptorData->ImageInfo; - break; + { + var texture = heapObject.Value as Texture; + descriptorData->ImageInfo = new VkDescriptorImageInfo { imageView = texture?.NativeImageView ?? GraphicsDevice.EmptyTexture.NativeImageView, imageLayout = VkImageLayout.ShaderReadOnlyOptimal }; + write->pImageInfo = &descriptorData->ImageInfo; + break; + } + + case VkDescriptorType.StorageImage: + { + var texture = heapObject.Value as Texture; + descriptorData->ImageInfo = new VkDescriptorImageInfo { imageView = texture?.NativeImageView ?? GraphicsDevice.EmptyTexture.NativeImageView, imageLayout = VkImageLayout.General }; + write->pImageInfo = &descriptorData->ImageInfo; + break; + } case VkDescriptorType.Sampler: var samplerState = heapObject.Value as SamplerState; @@ -349,7 +363,7 @@ private unsafe void PrepareDraw() case VkDescriptorType.UniformBuffer: var buffer = heapObject.Value as Buffer; - descriptorData->BufferInfo = new VkDescriptorBufferInfo { buffer = buffer?.NativeBuffer ?? VkBuffer.Null, offset = (ulong) heapObject.Offset, range = (ulong) heapObject.Size }; + descriptorData->BufferInfo = new VkDescriptorBufferInfo { buffer = buffer?.NativeBuffer ?? VkBuffer.Null, offset = (ulong)heapObject.Offset, range = (ulong)heapObject.Size }; write->pBufferInfo = &descriptorData->BufferInfo; break; @@ -364,9 +378,9 @@ private unsafe void PrepareDraw() } } - vkUpdateDescriptorSets(GraphicsDevice.NativeDevice, (uint) bindingCount, writes, descriptorCopyCount: 0, descriptorCopies: null); + vkUpdateDescriptorSets(GraphicsDevice.NativeDevice, (uint)bindingCount, writes, descriptorCopyCount: 0, descriptorCopies: null); #endif - vkCmdBindDescriptorSets(currentCommandList.NativeCommandBuffer, VkPipelineBindPoint.Graphics, activePipeline.NativeLayout, firstSet: 0, descriptorSetCount: 1, &localDescriptorSet, dynamicOffsetCount: 0, dynamicOffsets: null); + vkCmdBindDescriptorSets(currentCommandList.NativeCommandBuffer, activePipeline.IsCompute ? VkPipelineBindPoint.Compute : VkPipelineBindPoint.Graphics, activePipeline.NativeLayout, firstSet: 0, descriptorSetCount: 1, &localDescriptorSet, dynamicOffsetCount: 0, dynamicOffsets: null); } private readonly FastList copies = new(); @@ -390,7 +404,7 @@ public void SetPipelineState(PipelineState pipelineState) activePipeline = pipelineState; - vkCmdBindPipeline(currentCommandList.NativeCommandBuffer, VkPipelineBindPoint.Graphics, pipelineState.NativePipeline); + vkCmdBindPipeline(currentCommandList.NativeCommandBuffer, activePipeline.IsCompute ? VkPipelineBindPoint.Compute : VkPipelineBindPoint.Graphics, pipelineState.NativePipeline); } public unsafe void SetVertexBuffer(int index, Buffer buffer, int offset, int stride) @@ -446,7 +460,7 @@ public unsafe void ResourceBarrierTransition(GraphicsResource resource, Graphics case GraphicsResourceState.PixelShaderResource: texture.NativeLayout = VkImageLayout.ShaderReadOnlyOptimal; texture.NativeAccessMask = VkAccessFlags.ShaderRead; - texture.NativePipelineStageMask = VkPipelineStageFlags.FragmentShader; + texture.NativePipelineStageMask = VkPipelineStageFlags.FragmentShader | VkPipelineStageFlags.ComputeShader; // TODO: Not sure why I did this can probably double check ... break; case GraphicsResourceState.GenericRead: texture.NativeLayout = VkImageLayout.General; @@ -503,6 +517,9 @@ public void SetDescriptorSets(int index, DescriptorSet[] descriptorSets) /// public void Dispatch(int threadCountX, int threadCountY, int threadCountZ) { + CleanupRenderPass(); + BindDescriptorSets(); + vkCmdDispatch(currentCommandList.NativeCommandBuffer, (uint)threadCountX, (uint)threadCountY, (uint)threadCountZ); } /// @@ -512,6 +529,9 @@ public void Dispatch(int threadCountX, int threadCountY, int threadCountZ) /// The offset information bytes. public void Dispatch(Buffer indirectBuffer, int offsetInBytes) { + CleanupRenderPass(); + BindDescriptorSets(); + vkCmdDispatchIndirect(currentCommandList.NativeCommandBuffer, indirectBuffer.NativeBuffer, (ulong)offsetInBytes); } /// diff --git a/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs b/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs index 97b3bd53f1..4be30324f8 100644 --- a/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs +++ b/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs @@ -53,9 +53,9 @@ public partial class GraphicsDevice 256, // Sampler 0, // CombinedImageSampler 512, // SampledImage - 0, // StorageImage + 32, // StorageImage 64, // UniformTexelBuffer - 0, // StorageTexelBuffer + 32, // StorageTexelBuffer 512, // UniformBuffer 0, // StorageBuffer 0, // UniformBufferDynamic @@ -292,11 +292,22 @@ private unsafe void InitializePlatformDevice(GraphicsProfile[] graphicsProfiles, depthClamp = true, }; - Span supportedExtensionProperties = stackalloc VkUtf8String[] + if (graphicsProfiles.Any(x => x >= GraphicsProfile.Level_11_0)) + { + enabledFeature.shaderStorageImageReadWithoutFormat = true; + enabledFeature.shaderStorageImageWriteWithoutFormat = true; + } + + var extensionProperties = vkEnumerateDeviceExtensionProperties(NativePhysicalDevice); + var availableExtensionNames = new List(); + var desiredExtensionNames = new List(); + + fixed (VkExtensionProperties* extensionPropertiesPtr = extensionProperties) { VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_EXT_DEBUG_MARKER_EXTENSION_NAME, }; + Span supportedExtensionProperties = stackalloc VkUtf8String[] var availableExtensionProperties = GetAvailableExtensionProperties(supportedExtensionProperties); ValidateExtensionPropertiesAvailability(availableExtensionProperties); var desiredExtensionProperties = new HashSet diff --git a/sources/engine/Stride.Graphics/Vulkan/PipelineState.Vulkan.cs b/sources/engine/Stride.Graphics/Vulkan/PipelineState.Vulkan.cs index 7a17961b10..72047dd181 100644 --- a/sources/engine/Stride.Graphics/Vulkan/PipelineState.Vulkan.cs +++ b/sources/engine/Stride.Graphics/Vulkan/PipelineState.Vulkan.cs @@ -10,6 +10,7 @@ using Stride.Shaders; using Stride.Core.Serialization; using Encoding = System.Text.Encoding; +using System.IO.IsolatedStorage; namespace Stride.Graphics { @@ -25,6 +26,7 @@ public partial class PipelineState internal int[] ResourceGroupMapping; internal int ResourceGroupCount; internal PipelineStateDescription Description; + internal bool IsCompute; // State exposed by the CommandList private static readonly VkDynamicState[] dynamicStates = @@ -50,6 +52,7 @@ private unsafe void Recreate() fixed (void* defaultEntryPointData = defaultEntryPoint) // null if array is empty or null RecreateInner(); } + private unsafe void RecreateInner() { if (Description.RootSignature == null) @@ -62,150 +65,169 @@ private unsafe void RecreateInner() // Create shader stages var stages = CreateShaderStages(Description, out var inputAttributeNames); - var inputAttributes = new VkVertexInputAttributeDescription[Description.InputElements.Length]; - int inputAttributeCount = 0; - var inputBindings = new VkVertexInputBindingDescription[inputAttributes.Length]; - int inputBindingCount = 0; - - for (int inputElementIndex = 0; inputElementIndex < inputAttributes.Length; inputElementIndex++) + if (IsCompute) { - var inputElement = Description.InputElements[inputElementIndex]; - var slotIndex = inputElement.InputSlot; - - if (inputElement.InstanceDataStepRate > 1) - { - throw new NotImplementedException(); - } - - VulkanConvertExtensions.ConvertPixelFormat(inputElement.Format, out var format, out var size, out var isCompressed); - - var location = inputAttributeNames.FirstOrDefault(x => x.Value == inputElement.SemanticName && inputElement.SemanticIndex == 0 || x.Value == inputElement.SemanticName + inputElement.SemanticIndex); - if (location.Value != null) + fixed (VkPipelineShaderStageCreateInfo* fStages = stages) { - inputAttributes[inputAttributeCount++] = new VkVertexInputAttributeDescription + var createInfo = new VkComputePipelineCreateInfo { - format = format, - offset = (uint) inputElement.AlignedByteOffset, - binding = (uint) inputElement.InputSlot, - location = (uint) location.Key + sType = VkStructureType.ComputePipelineCreateInfo, + layout = NativeLayout, + stage = *fStages, // Can't be null if IsCompute == true. + pNext = null }; - } - inputBindings[slotIndex].binding = (uint) slotIndex; - inputBindings[slotIndex].inputRate = inputElement.InputSlotClass == InputClassification.Vertex ? VkVertexInputRate.Vertex : VkVertexInputRate.Instance; - - // TODO VULKAN: This is currently an argument to Draw() overloads. - if (inputBindings[slotIndex].stride < inputElement.AlignedByteOffset + size) - inputBindings[slotIndex].stride = (uint) (inputElement.AlignedByteOffset + size); - - if (inputElement.InputSlot >= inputBindingCount) - inputBindingCount = inputElement.InputSlot + 1; + fixed (VkPipeline* nativePipelinePtr = &NativePipeline) + vkCreateComputePipelines(GraphicsDevice.NativeDevice, VkPipelineCache.Null, 1, &createInfo, allocator: null, nativePipelinePtr); + } } - - var inputAssemblyState = new VkPipelineInputAssemblyStateCreateInfo + else { - sType = VkStructureType.PipelineInputAssemblyStateCreateInfo, - topology = VulkanConvertExtensions.ConvertPrimitiveType(Description.PrimitiveType), - primitiveRestartEnable = VulkanConvertExtensions.ConvertPrimitiveRestart(Description.PrimitiveType) - }; + var inputAttributes = new VkVertexInputAttributeDescription[Description.InputElements.Length]; + int inputAttributeCount = 0; + var inputBindings = new VkVertexInputBindingDescription[inputAttributes.Length]; + int inputBindingCount = 0; - // TODO VULKAN: Tessellation and multisampling - var multisampleState = new VkPipelineMultisampleStateCreateInfo - { - sType = VkStructureType.PipelineMultisampleStateCreateInfo, - rasterizationSamples = VkSampleCountFlags.Count1 - }; + for (int inputElementIndex = 0; inputElementIndex < inputAttributes.Length; inputElementIndex++) + { + var inputElement = Description.InputElements[inputElementIndex]; + var slotIndex = inputElement.InputSlot; - var tessellationState = new VkPipelineTessellationStateCreateInfo - { - sType = VkStructureType.PipelineTessellationStateCreateInfo - }; + if (inputElement.InstanceDataStepRate > 1) + { + throw new NotImplementedException(); + } - var rasterizationState = CreateRasterizationState(Description.RasterizerState); + VulkanConvertExtensions.ConvertPixelFormat(inputElement.Format, out var format, out var size, out var isCompressed); - var depthStencilState = CreateDepthStencilState(Description); + var location = inputAttributeNames.FirstOrDefault(x => x.Value == inputElement.SemanticName && inputElement.SemanticIndex == 0 || x.Value == inputElement.SemanticName + inputElement.SemanticIndex); + if (location.Value != null) + { + inputAttributes[inputAttributeCount++] = new VkVertexInputAttributeDescription + { + format = format, + offset = (uint)inputElement.AlignedByteOffset, + binding = (uint)inputElement.InputSlot, + location = (uint)location.Key + }; + } - var description = Description.BlendState; + inputBindings[slotIndex].binding = (uint)slotIndex; + inputBindings[slotIndex].inputRate = inputElement.InputSlotClass == InputClassification.Vertex ? VkVertexInputRate.Vertex : VkVertexInputRate.Instance; - var renderTargetCount = Description.Output.RenderTargetCount; - var colorBlendAttachments = new VkPipelineColorBlendAttachmentState[renderTargetCount]; + // TODO VULKAN: This is currently an argument to Draw() overloads. + if (inputBindings[slotIndex].stride < inputElement.AlignedByteOffset + size) + inputBindings[slotIndex].stride = (uint)(inputElement.AlignedByteOffset + size); - var renderTargetBlendState = &description.RenderTarget0; - for (int i = 0; i < renderTargetCount; i++) - { - colorBlendAttachments[i] = new VkPipelineColorBlendAttachmentState + if (inputElement.InputSlot >= inputBindingCount) + inputBindingCount = inputElement.InputSlot + 1; + } + + var inputAssemblyState = new VkPipelineInputAssemblyStateCreateInfo { - blendEnable = renderTargetBlendState->BlendEnable, - alphaBlendOp = VulkanConvertExtensions.ConvertBlendFunction(renderTargetBlendState->AlphaBlendFunction), - colorBlendOp = VulkanConvertExtensions.ConvertBlendFunction(renderTargetBlendState->ColorBlendFunction), - dstAlphaBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->AlphaDestinationBlend), - dstColorBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->ColorDestinationBlend), - srcAlphaBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->AlphaSourceBlend), - srcColorBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->ColorSourceBlend), - colorWriteMask = VulkanConvertExtensions.ConvertColorWriteChannels(renderTargetBlendState->ColorWriteChannels) + sType = VkStructureType.PipelineInputAssemblyStateCreateInfo, + topology = VulkanConvertExtensions.ConvertPrimitiveType(Description.PrimitiveType), + primitiveRestartEnable = VulkanConvertExtensions.ConvertPrimitiveRestart(Description.PrimitiveType) }; - if (description.IndependentBlendEnable) - renderTargetBlendState++; - } - - var viewportState = new VkPipelineViewportStateCreateInfo - { - sType = VkStructureType.PipelineViewportStateCreateInfo, - scissorCount = 1, - viewportCount = 1 - }; - - // fixed yields null if array is empty or null - fixed (VkDynamicState* dynamicStatesPointer = dynamicStates) - fixed (VkVertexInputAttributeDescription* fInputAttributes = inputAttributes) - fixed (VkVertexInputBindingDescription* fInputBindings = inputBindings) - fixed (VkPipelineColorBlendAttachmentState* fColorBlendAttachments = colorBlendAttachments) - fixed (VkPipelineShaderStageCreateInfo* fStages = stages) - { - var vertexInputState = new VkPipelineVertexInputStateCreateInfo + // TODO VULKAN: Tessellation and multisampling + var multisampleState = new VkPipelineMultisampleStateCreateInfo { - sType = VkStructureType.PipelineVertexInputStateCreateInfo, - vertexAttributeDescriptionCount = (uint) inputAttributeCount, - pVertexAttributeDescriptions = fInputAttributes, - vertexBindingDescriptionCount = (uint) inputBindingCount, - pVertexBindingDescriptions = fInputBindings + sType = VkStructureType.PipelineMultisampleStateCreateInfo, + rasterizationSamples = VkSampleCountFlags.Count1 }; - var colorBlendState = new VkPipelineColorBlendStateCreateInfo + var tessellationState = new VkPipelineTessellationStateCreateInfo { - sType = VkStructureType.PipelineColorBlendStateCreateInfo, - attachmentCount = (uint) renderTargetCount, - pAttachments = fColorBlendAttachments + sType = VkStructureType.PipelineTessellationStateCreateInfo }; - var dynamicState = new VkPipelineDynamicStateCreateInfo + var rasterizationState = CreateRasterizationState(Description.RasterizerState); + + var depthStencilState = CreateDepthStencilState(Description); + + var description = Description.BlendState; + + var renderTargetCount = Description.Output.RenderTargetCount; + var colorBlendAttachments = new VkPipelineColorBlendAttachmentState[renderTargetCount]; + + var renderTargetBlendState = &description.RenderTarget0; + for (int i = 0; i < renderTargetCount; i++) { - sType = VkStructureType.PipelineDynamicStateCreateInfo, - dynamicStateCount = (uint) dynamicStates.Length, - pDynamicStates = dynamicStatesPointer - }; + colorBlendAttachments[i] = new VkPipelineColorBlendAttachmentState + { + blendEnable = renderTargetBlendState->BlendEnable, + alphaBlendOp = VulkanConvertExtensions.ConvertBlendFunction(renderTargetBlendState->AlphaBlendFunction), + colorBlendOp = VulkanConvertExtensions.ConvertBlendFunction(renderTargetBlendState->ColorBlendFunction), + dstAlphaBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->AlphaDestinationBlend), + dstColorBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->ColorDestinationBlend), + srcAlphaBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->AlphaSourceBlend), + srcColorBlendFactor = VulkanConvertExtensions.ConvertBlend(renderTargetBlendState->ColorSourceBlend), + colorWriteMask = VulkanConvertExtensions.ConvertColorWriteChannels(renderTargetBlendState->ColorWriteChannels) + }; + + if (description.IndependentBlendEnable) + renderTargetBlendState++; + } - var createInfo = new VkGraphicsPipelineCreateInfo + var viewportState = new VkPipelineViewportStateCreateInfo { - sType = VkStructureType.GraphicsPipelineCreateInfo, - layout = NativeLayout, - stageCount = (uint) stages.Length, - pStages = stages.Length > 0 ? fStages : null, - //tessellationState = &tessellationState, - pVertexInputState = &vertexInputState, - pInputAssemblyState = &inputAssemblyState, - pRasterizationState = &rasterizationState, - pMultisampleState = &multisampleState, - pDepthStencilState = &depthStencilState, - pColorBlendState = &colorBlendState, - pDynamicState = &dynamicState, - pViewportState = &viewportState, - renderPass = NativeRenderPass, - subpass = 0 + sType = VkStructureType.PipelineViewportStateCreateInfo, + scissorCount = 1, + viewportCount = 1 }; - fixed (VkPipeline* nativePipelinePtr = &NativePipeline) - vkCreateGraphicsPipelines(GraphicsDevice.NativeDevice, VkPipelineCache.Null, createInfoCount: 1, &createInfo, allocator: null, nativePipelinePtr); + + // fixed yields null if array is empty or null + fixed (VkDynamicState* dynamicStatesPointer = dynamicStates) + fixed (VkVertexInputAttributeDescription* fInputAttributes = inputAttributes) + fixed (VkVertexInputBindingDescription* fInputBindings = inputBindings) + fixed (VkPipelineColorBlendAttachmentState* fColorBlendAttachments = colorBlendAttachments) + fixed (VkPipelineShaderStageCreateInfo* fStages = stages) + { + var vertexInputState = new VkPipelineVertexInputStateCreateInfo + { + sType = VkStructureType.PipelineVertexInputStateCreateInfo, + vertexAttributeDescriptionCount = (uint)inputAttributeCount, + pVertexAttributeDescriptions = fInputAttributes, + vertexBindingDescriptionCount = (uint)inputBindingCount, + pVertexBindingDescriptions = fInputBindings + }; + + var colorBlendState = new VkPipelineColorBlendStateCreateInfo + { + sType = VkStructureType.PipelineColorBlendStateCreateInfo, + attachmentCount = (uint)renderTargetCount, + pAttachments = fColorBlendAttachments + }; + + var dynamicState = new VkPipelineDynamicStateCreateInfo + { + sType = VkStructureType.PipelineDynamicStateCreateInfo, + dynamicStateCount = (uint)dynamicStates.Length, + pDynamicStates = dynamicStatesPointer + }; + + var createInfo = new VkGraphicsPipelineCreateInfo + { + sType = VkStructureType.GraphicsPipelineCreateInfo, + layout = NativeLayout, + stageCount = (uint)stages.Length, + pStages = stages.Length > 0 ? fStages : null, + //tessellationState = &tessellationState, + pVertexInputState = &vertexInputState, + pInputAssemblyState = &inputAssemblyState, + pRasterizationState = &rasterizationState, + pMultisampleState = &multisampleState, + pDepthStencilState = &depthStencilState, + pColorBlendState = &colorBlendState, + pDynamicState = &dynamicState, + pViewportState = &viewportState, + renderPass = NativeRenderPass, + subpass = 0 + }; + fixed (VkPipeline* nativePipelinePtr = &NativePipeline) + vkCreateGraphicsPipelines(GraphicsDevice.NativeDevice, VkPipelineCache.Null, createInfoCount: 1, &createInfo, allocator: null, nativePipelinePtr); + } } // Cleanup shader modules @@ -256,7 +278,7 @@ private unsafe void CreateRenderPass(PipelineStateDescription pipelineStateDescr colorAttachmentReferences[i] = new VkAttachmentReference { - attachment = (uint) i, + attachment = (uint)i, layout = VkImageLayout.ColorAttachmentOptimal }; } @@ -279,17 +301,18 @@ private unsafe void CreateRenderPass(PipelineStateDescription pipelineStateDescr // fixed yields null if array is empty or null fixed (VkAttachmentReference* fColorAttachmentReferences = colorAttachmentReferences) - fixed (VkAttachmentDescription* fAttachments = attachments) { + fixed (VkAttachmentDescription* fAttachments = attachments) + { var depthAttachmentReference = new VkAttachmentReference { - attachment = (uint) attachments.Length - 1, + attachment = (uint)attachments.Length - 1, layout = VkImageLayout.DepthStencilAttachmentOptimal }; var subpass = new VkSubpassDescription { pipelineBindPoint = VkPipelineBindPoint.Graphics, - colorAttachmentCount = (uint) renderTargetCount, + colorAttachmentCount = (uint)renderTargetCount, pColorAttachments = fColorAttachmentReferences, pDepthStencilAttachment = hasDepthStencilAttachment ? &depthAttachmentReference : null }; @@ -297,7 +320,7 @@ private unsafe void CreateRenderPass(PipelineStateDescription pipelineStateDescr var renderPassCreateInfo = new VkRenderPassCreateInfo { sType = VkStructureType.RenderPassCreateInfo, - attachmentCount = (uint) attachmentCount, + attachmentCount = (uint)attachmentCount, pAttachments = fAttachments, subpassCount = 1, pSubpasses = &subpass @@ -417,6 +440,8 @@ private unsafe VkPipelineShaderStageCreateInfo[] CreateShaderStages(PipelineStat var stages = pipelineStateDescription.EffectBytecode.Stages; var nativeStages = new VkPipelineShaderStageCreateInfo[stages.Length]; + IsCompute = false; + inputAttributeNames = null; for (int i = 0; i < stages.Length; i++) @@ -424,6 +449,8 @@ private unsafe VkPipelineShaderStageCreateInfo[] CreateShaderStages(PipelineStat var shaderBytecode = ReadShaderBytecode(stages[i].Data); if (stages[i].Stage == ShaderStage.Vertex) inputAttributeNames = shaderBytecode.InputAttributeNames; + if (stages[i].Stage == ShaderStage.Compute) + IsCompute = true; fixed (byte* entryPointPointer = &defaultEntryPoint[0]) { @@ -436,7 +463,7 @@ private unsafe VkPipelineShaderStageCreateInfo[] CreateShaderStages(PipelineStat }; vkCreateShaderModule(GraphicsDevice.NativeDevice, shaderBytecode.Data, allocator: null, out nativeStages[i].module); } - }; + } return nativeStages; } diff --git a/sources/engine/Stride.Graphics/Vulkan/VulkanConvertExtensions.cs b/sources/engine/Stride.Graphics/Vulkan/VulkanConvertExtensions.cs index 5c6b472d8e..78989269c9 100644 --- a/sources/engine/Stride.Graphics/Vulkan/VulkanConvertExtensions.cs +++ b/sources/engine/Stride.Graphics/Vulkan/VulkanConvertExtensions.cs @@ -545,6 +545,11 @@ public static VkDescriptorType ConvertDescriptorType(EffectParameterClass @class case EffectParameterType.Texture1DArray: case EffectParameterType.Texture2DArray: case EffectParameterType.TextureCubeArray: + case EffectParameterType.RWTexture1D: + case EffectParameterType.RWTexture1DArray: + case EffectParameterType.RWTexture2D: + case EffectParameterType.RWTexture2DArray: + case EffectParameterType.RWTexture3D: return VkDescriptorType.SampledImage; case EffectParameterType.Buffer: @@ -564,6 +569,12 @@ public static VkDescriptorType ConvertDescriptorType(EffectParameterClass @class case EffectParameterType.Texture1DArray: case EffectParameterType.Texture2DArray: case EffectParameterType.TextureCubeArray: + case EffectParameterType.RWTexture1D: + case EffectParameterType.RWTexture1DArray: + case EffectParameterType.RWTexture2D: + case EffectParameterType.RWTexture2DArray: + case EffectParameterType.RWTexture3D: + case EffectParameterType.RWBuffer: return VkDescriptorType.StorageImage; case EffectParameterType.Buffer: diff --git a/sources/engine/Stride.Rendering/Rendering/Images/ColorTransforms/ColorTransformGroup.cs b/sources/engine/Stride.Rendering/Rendering/Images/ColorTransforms/ColorTransformGroup.cs index 7f17a3b1fb..77a93a12ce 100644 --- a/sources/engine/Stride.Rendering/Rendering/Images/ColorTransforms/ColorTransformGroup.cs +++ b/sources/engine/Stride.Rendering/Rendering/Images/ColorTransforms/ColorTransformGroup.cs @@ -116,7 +116,7 @@ public ColorTransformCollection PostTransforms } } - protected override void DrawCore(RenderDrawContext context1) + protected override void DrawCore(RenderDrawContext context) { var output = GetOutput(0); if (output == null) @@ -125,14 +125,15 @@ protected override void DrawCore(RenderDrawContext context1) } // Collect all transform parameters - CollectTransformsParameters(context1); + CollectTransformsParameters(context); for (int i = 0; i < transformContext.Inputs.Count; i++) { transformGroupEffect.SetInput(i, transformContext.Inputs[i]); + context.CommandList.ResourceBarrierTransition(transformContext.Inputs[i], Graphics.GraphicsResourceState.PixelShaderResource); } transformGroupEffect.SetOutput(output); - transformGroupEffect.Draw(context1, name: Name); + transformGroupEffect.Draw(context, name: Name); } protected virtual void CollectPreTransforms() diff --git a/sources/engine/Stride.Rendering/Rendering/Images/PostProcessingEffects.cs b/sources/engine/Stride.Rendering/Rendering/Images/PostProcessingEffects.cs index becfa42d68..a422174d87 100644 --- a/sources/engine/Stride.Rendering/Rendering/Images/PostProcessingEffects.cs +++ b/sources/engine/Stride.Rendering/Rendering/Images/PostProcessingEffects.cs @@ -449,6 +449,9 @@ protected override void DrawCore(RenderDrawContext context) // Set this parameter that will be used by the tone mapping colorTransformsGroup.Parameters.Set(LuminanceEffect.LuminanceResult, new LuminanceResult(luminanceEffect.AverageLuminance, luminanceTexture)); + + if (luminanceTexture != null) + context.CommandList.ResourceBarrierTransition(luminanceTexture, GraphicsResourceState.PixelShaderResource); } if (BrightFilter.Enabled && (Bloom.Enabled || LightStreak.Enabled || LensFlare.Enabled)) diff --git a/sources/engine/Stride.Shaders.Compiler/OpenGL/ShaderCompiler.cs b/sources/engine/Stride.Shaders.Compiler/OpenGL/ShaderCompiler.cs index b896e65bb3..d84dc39dd3 100644 --- a/sources/engine/Stride.Shaders.Compiler/OpenGL/ShaderCompiler.cs +++ b/sources/engine/Stride.Shaders.Compiler/OpenGL/ShaderCompiler.cs @@ -193,6 +193,9 @@ private string Compile(string shaderSource, string entryPoint, ShaderStage stage case ShaderStage.Domain: shaderBytecodeResult.Error("Domain stage can't be converted to OpenGL. Only Vertex and Pixel shaders are supported"); break; + case ShaderStage.Compute when shaderPlatform == GlslShaderPlatform.Vulkan: + pipelineStage = PipelineStage.Compute; + break; case ShaderStage.Compute: shaderBytecodeResult.Error("Compute stage can't be converted to OpenGL. Only Vertex and Pixel shaders are supported"); break; @@ -362,6 +365,11 @@ private string Compile(string shaderSource, string entryPoint, ShaderStage stage glslShaderWriter.ExtraHeaders = "#define texelFetchBufferPlaceholder"; } + if (shaderPlatform == GlslShaderPlatform.Vulkan && pipelineStage == PipelineStage.Compute) + { + glslShaderWriter.Extensions.Add("GL_EXT_shader_image_load_formatted"); + } + // Write shader glslShaderWriter.Visit(glslShader); diff --git a/sources/engine/Stride.Shaders.Parser/Mixins/StrideShaderMixer.cs b/sources/engine/Stride.Shaders.Parser/Mixins/StrideShaderMixer.cs index 1dc7e32bab..2d1bdb6223 100644 --- a/sources/engine/Stride.Shaders.Parser/Mixins/StrideShaderMixer.cs +++ b/sources/engine/Stride.Shaders.Parser/Mixins/StrideShaderMixer.cs @@ -1319,7 +1319,12 @@ private void RemoveUselessVariables() /// true/false private bool IsOutOfCBufferVariable(Variable variable) { - return variable.Type.IsSamplerType() || variable.Type is TextureType || variable.Type.IsStateType() || variable.Type.ResolveType() is ObjectType; + return variable.Type.IsSamplerType() + || variable.Type is TextureType + || variable.Type.IsStateType() + || variable.Type.ResolveType() is ObjectType + || variable.Qualifiers.Contains(StorageQualifier.Shared) + || variable.Qualifiers.Contains(StorageQualifier.GroupShared); } /// @@ -1329,7 +1334,13 @@ private bool IsOutOfCBufferVariable(Variable variable) /// true/false private bool KeepVariableInCBuffer(Variable variable) { - return !(variable.Qualifiers.Contains(Stride.Core.Shaders.Ast.Hlsl.StorageQualifier.Extern) || variable.Qualifiers.Contains(StrideStorageQualifier.Stream) || variable.Qualifiers.Contains(StrideStorageQualifier.PatchStream) || IsOutOfCBufferVariable(variable) || variable.Qualifiers.Contains(StorageQualifier.Const)); + return !(variable.Qualifiers.Contains(Stride.Core.Shaders.Ast.Hlsl.StorageQualifier.Extern) + || variable.Qualifiers.Contains(StrideStorageQualifier.Stream) + || variable.Qualifiers.Contains(StrideStorageQualifier.PatchStream) + || IsOutOfCBufferVariable(variable) + || variable.Qualifiers.Contains(StorageQualifier.Const) + || variable.Qualifiers.Contains(StorageQualifier.Shared) + || variable.Qualifiers.Contains(StorageQualifier.GroupShared)); } // Group everything by constant buffers diff --git a/sources/shaders/Stride.Core.Shaders/Ast/StorageQualifier.cs b/sources/shaders/Stride.Core.Shaders/Ast/StorageQualifier.cs index 330f4d2aec..9850ee3964 100644 --- a/sources/shaders/Stride.Core.Shaders/Ast/StorageQualifier.cs +++ b/sources/shaders/Stride.Core.Shaders/Ast/StorageQualifier.cs @@ -21,6 +21,26 @@ public partial class StorageQualifier /// public static readonly Qualifier Uniform = new Qualifier("uniform"); + /// + /// Shared qualifier. + /// + public static readonly Qualifier Shared = new Qualifier("shared"); + + /// + /// Shared qualifier. + /// + public static readonly Qualifier GroupShared = new Qualifier("groupshared"); + + /// + /// Writeonly qualifier. + /// + public static readonly Qualifier WriteOnly = new Qualifier("writeonly"); + + /// + /// Readonly qualifier. + /// + public static readonly Qualifier ReadOnly = new Qualifier("readonly"); + #endregion #region Public Methods diff --git a/sources/shaders/Stride.Core.Shaders/Convertor/GlobalUniformVisitor.cs b/sources/shaders/Stride.Core.Shaders/Convertor/GlobalUniformVisitor.cs index 71901f2ace..473d3228d7 100644 --- a/sources/shaders/Stride.Core.Shaders/Convertor/GlobalUniformVisitor.cs +++ b/sources/shaders/Stride.Core.Shaders/Convertor/GlobalUniformVisitor.cs @@ -102,7 +102,9 @@ private Variable GetUniform(Expression expression) // If the variable is a global uniform, non static/const and is not already in the list used then return (variable != null && shader.Declarations.Contains(variable) && !variable.Qualifiers.Contains(Ast.Hlsl.StorageQualifier.Static) - && !variable.Qualifiers.Contains(Ast.StorageQualifier.Const)) + && !variable.Qualifiers.Contains(Ast.StorageQualifier.Const) + && !variable.Qualifiers.Contains(Ast.StorageQualifier.Shared) + && !variable.Qualifiers.Contains(Ast.StorageQualifier.GroupShared)) ? variable : null; } @@ -192,7 +194,11 @@ public override Node Visit(AssignmentExpression assignmentExpression) } else { - UniformUsedWriteFirstList.Add(variable); + var variableType = variable.Type.ResolveType(); + if (!variableType.Name.Text.StartsWith("RWTexture") && !variableType.Name.Text.StartsWith("RWBuffer")) + { + UniformUsedWriteFirstList.Add(variable); + } } } if (assignmentExpression.Operator != AssignmentOperator.Default) diff --git a/sources/shaders/Stride.Core.Shaders/Convertor/HlslToGlslConvertor.cs b/sources/shaders/Stride.Core.Shaders/Convertor/HlslToGlslConvertor.cs index a06cfed6fc..0f2653339f 100644 --- a/sources/shaders/Stride.Core.Shaders/Convertor/HlslToGlslConvertor.cs +++ b/sources/shaders/Stride.Core.Shaders/Convertor/HlslToGlslConvertor.cs @@ -142,7 +142,7 @@ public HlslToGlslConvertor(GlslShaderPlatform shaderPlatform, int shaderVersion, { case PipelineStage.Vertex: builtinInputs.Add("SV_VertexID", isVulkan ? "gl_VertexIndex" : "gl_VertexID"); - builtinInputs.Add("SV_InstanceID", isVulkan ? "gl_InstanceIndex" : "gl_InstanceID"); + builtinInputs.Add("SV_InstanceID", isVulkan ? "gl_InstanceIndex" : "gl_InstanceID"); if (shaderModel < ShaderModel.Model40) { builtinOutputs.Add("POSITION", "gl_Position"); @@ -188,16 +188,22 @@ public HlslToGlslConvertor(GlslShaderPlatform shaderPlatform, int shaderVersion, builtinOutputs.Add("SV_Target", "gl_FragData[]"); } break; + case PipelineStage.Compute: + builtinInputs.Add("SV_DispatchThreadID", "gl_GlobalInvocationID"); + builtinInputs.Add("SV_GroupID", "gl_WorkGroupID"); + builtinInputs.Add("SV_GroupIndex", "gl_LocalInvocationIndex"); + builtinInputs.Add("SV_GroupThreadID", "gl_LocalInvocationID"); + break; } - builtinGlslTypes = new Dictionary(StringComparer.CurrentCultureIgnoreCase) + builtinGlslTypes = new Dictionary(StringComparer.CurrentCultureIgnoreCase) { { "gl_ClipDistance", ScalarType.Float}, // array { "gl_FragCoord", VectorType.Float4}, - { "gl_FragDepth", ScalarType.Float}, - { "gl_FragColor", VectorType.Float4}, + { "gl_FragDepth", ScalarType.Float}, + { "gl_FragColor", VectorType.Float4}, { "gl_FragData", VectorType.Float4}, // array - { "gl_FrontFacing", ScalarType.Bool}, + { "gl_FrontFacing", ScalarType.Bool}, { "gl_InstanceID", ScalarType.Int }, { "gl_InstanceIndex", ScalarType.Int }, { "gl_InvocationID", ScalarType.Int}, @@ -217,21 +223,27 @@ public HlslToGlslConvertor(GlslShaderPlatform shaderPlatform, int shaderVersion, { "gl_VertexID", ScalarType.Int}, { "gl_VertexIndex", ScalarType.Int}, { "gl_ViewportIndex", ScalarType.Int}, + { "gl_GlobalInvocationID", VectorType.UInt3}, + { "gl_WorkGroupID", VectorType.UInt3}, + { "gl_LocalInvocationIndex", ScalarType.UInt}, + { "gl_LocalInvocationID", VectorType.UInt3}, }; } - functionMapping = new Dictionary { - { "ddx", "dFdx" }, - { "ddy", "dFdy" }, - { "fmod", "mod" }, - { "frac", "fract" }, - { "lerp", "mix" }, - { "rsqrt", "inversesqrt" }, - { "atan2", "atan" }, - { "saturate", "clamp" }, - //{ "D3DCOLORtoUBYTE4", "ivec4" }, - }; - } + functionMapping = new Dictionary + { + { "ddx", "dFdx" }, + { "ddy", "dFdy" }, + { "fmod", "mod" }, + { "frac", "fract" }, + { "lerp", "mix" }, + { "rsqrt", "inversesqrt" }, + { "atan2", "atan" }, + { "saturate", "clamp" }, + { "GroupMemoryBarrier", "groupMemoryBarrier" }, + //{ "D3DCOLORtoUBYTE4", "ivec4" }, + }; +} #endregion @@ -413,7 +425,7 @@ public static void Prepare(Shader shader) // Replace all Half types to float, as there are no equivalent in glsl // This will force the type inference analysis to use float instead of half SearchVisitor.Run( - shader, + shader, node => { if (node.Equals(ScalarType.Half)) @@ -782,8 +794,9 @@ protected void PostVisitEntryPoint(MethodDefinition function) 0, new ExpressionStatement( new AssignmentExpression( - AssignmentOperator.Default, fieldRef.GetMemberReference(new VariableReferenceExpression(variable.Name)), - this.CastSemanticToReferenceType(variableFromSemantic.Name, fieldType, variableFromSemantic))) { Span = variable.Span }); + AssignmentOperator.Default, fieldRef.GetMemberReference(new VariableReferenceExpression(variable.Name)), + this.CastSemanticToReferenceType(variableFromSemantic.Name, fieldType, variableFromSemantic))) + { Span = variable.Span }); semanticFound = true; } } @@ -1057,6 +1070,12 @@ protected Statement VisitStatementAsFunctionInvocation(ExpressionStatement state } break; + + case "GroupMemoryBarrierWithGroupSync": + // GroupMemoryBarrierWithGroupSync => groupMemoryBarrier(); barrier(); + return new StatementList( + new ExpressionStatement(new MethodInvocationExpression("groupMemoryBarrier")), + new ExpressionStatement(new MethodInvocationExpression("barrier"))); } return null; @@ -1200,6 +1219,47 @@ protected Statement VisitStatementAsAssignExpression(Statement statement, Assign return resultBlock; } } + else if ((variableType.Name.Text.StartsWith("RWTexture") || variableType.Name.Text.StartsWith("RWBuffer")) && variableType is ClassType classType) + { + // Manually visit all sub expressions. + indexerExpression.Target = (Expression)VisitDynamic(indexerExpression.Target); + indexerExpression.Index = (Expression)VisitDynamic(indexerExpression.Index); + assignmentExpression.Value = (Expression)VisitDynamic(assignmentExpression.Value); + + // Convert assignment to imageStore, and cast the indexer to an appropriate integer type. + TypeBase indexerType = variableType.Name.Text switch + { + "RWTexture" => ScalarType.Int, + "RWBuffer" => ScalarType.Int, + "RWTexture2D" => VectorType.Int2, + "RWTexture3D" => VectorType.Int3, + _ => throw new NotSupportedException($"imageStore not supported for {variable.Name.Text}") + }; + + var indexer = new MethodInvocationExpression(new TypeReferenceExpression(indexerType), indexerExpression.Index); + + // Assignemnt should be cast to gvec4 for all formats so we have to figure out target type. + var classTypeName = classType.GenericArguments[0].Name.Text; + VectorType assignemntTargetType; + if (classTypeName.StartsWith("float")) + assignemntTargetType = VectorType.Float4; + else if (classTypeName.StartsWith("int")) + assignemntTargetType = VectorType.Int4; + else if (classTypeName.StartsWith("uint")) + assignemntTargetType = VectorType.UInt4; + else + throw new NotSupportedException($"{classTypeName} not supported for imageStore"); + + var assignment = new MethodInvocationExpression(new TypeReferenceExpression(assignemntTargetType), assignmentExpression.Value); + + // Fill out any missing arguments for the constructor so that a gvec4 can successfully constructed. + var lastCharacter = assignmentExpression.TypeInference.TargetType.Name.Text.Last(); + var dimensions = char.IsNumber(lastCharacter) ? lastCharacter - 48 : 1; + for (var i = dimensions; i < 4; i++) + assignment.Arguments.Add(new LiteralExpression(new Literal(0))); + + return new ExpressionStatement(new MethodInvocationExpression("imageStore", indexerExpression.Target, indexer, assignment)); + } } } @@ -1245,8 +1305,8 @@ public override Node Visit(MethodInvocationExpression methodInvocationExpression var leftParameter = ConvertToSafeExpressionForBinary(methodInvocationExpression.Arguments[NoSwapForBinaryMatrixOperation ? 0 : 1]); var rightParameter = ConvertToSafeExpressionForBinary(methodInvocationExpression.Arguments[NoSwapForBinaryMatrixOperation ? 1 : 0]); return new ParenthesizedExpression(new BinaryExpression(BinaryOperator.Multiply, leftParameter, rightParameter)); - } - + } + if (methodName == "lit") { // http://msdn.microsoft.com/en-us/library/bb509619%28v=vs.85%29.aspx @@ -1260,8 +1320,8 @@ public override Node Visit(MethodInvocationExpression methodInvocationExpression methodLit.Arguments.Add(new LiteralExpression(1.0f)); var diffuseArg = new ConditionalExpression( - new BinaryExpression(BinaryOperator.Less, methodInvocationExpression.Arguments[0], new LiteralExpression(0.0f)), - new LiteralExpression(0.0f), + new BinaryExpression(BinaryOperator.Less, methodInvocationExpression.Arguments[0], new LiteralExpression(0.0f)), + new LiteralExpression(0.0f), methodInvocationExpression.Arguments[0]); methodLit.Arguments.Add(diffuseArg); @@ -1269,10 +1329,10 @@ public override Node Visit(MethodInvocationExpression methodInvocationExpression var specularArg = new ConditionalExpression( new BinaryExpression( - BinaryOperator.LogicalOr, - new BinaryExpression(BinaryOperator.Less, methodInvocationExpression.Arguments[0], new LiteralExpression(0.0f)), - new BinaryExpression(BinaryOperator.Less, methodInvocationExpression.Arguments[1], new LiteralExpression(0.0f))), - new LiteralExpression(0.0f), + BinaryOperator.LogicalOr, + new BinaryExpression(BinaryOperator.Less, methodInvocationExpression.Arguments[0], new LiteralExpression(0.0f)), + new BinaryExpression(BinaryOperator.Less, methodInvocationExpression.Arguments[1], new LiteralExpression(0.0f))), + new LiteralExpression(0.0f), new MethodInvocationExpression("pow", methodInvocationExpression.Arguments[1], methodInvocationExpression.Arguments[2])); methodLit.Arguments.Add(specularArg); @@ -1317,7 +1377,7 @@ public override Node Visit(MethodInvocationExpression methodInvocationExpression if (string.Compare(methodName, "D3DCOLORtoUBYTE4", StringComparison.OrdinalIgnoreCase) == 0) { - return new MethodInvocationExpression(new TypeReferenceExpression(VectorType.Int4), methodInvocationExpression.Arguments[0]) { TypeInference = { TargetType = VectorType.Int4 }}; + return new MethodInvocationExpression(new TypeReferenceExpression(VectorType.Int4), methodInvocationExpression.Arguments[0]) { TypeInference = { TargetType = VectorType.Int4 } }; } string methodNameGl; @@ -1340,12 +1400,12 @@ public override Node Visit(MethodInvocationExpression methodInvocationExpression switch (memberReferenceExpression.Member) { - // Geometry shader + // Geometry shader case "RestartStrip": methodInvocationExpression.Target = new VariableReferenceExpression("EndPrimitive"); break; - // Texture object + // Texture object case "GetDimensions": // We should not be here parserResult.Error("GetDimensions should have been already preprocessed for expression [{0}]", methodInvocationExpression.Span, methodInvocationExpression); @@ -1465,7 +1525,7 @@ public override Node Visit(MethodInvocationExpression methodInvocationExpression // Since Texture.Load works with integer coordinates, need to convert texture.Load(coords, [offset]) to: // - textureLod[Offset](texture_sampler, coords.xy / textureSize(texture_sampler), coords.z, [offset]) on OpenGL ES 2 // - texelFetch[Offset](texture_sampler, coords.xy, coords.z, [offset]) on OpenGL and ES 3 - + string dimP = "??"; string mipLevel = "?"; @@ -1492,7 +1552,7 @@ public override Node Visit(MethodInvocationExpression methodInvocationExpression mipLevel = "w"; break; default: - parserResult.Error("Unable to process texture coordinates for type [{0}] when processing expression [{1}]", methodInvocationExpression.Span, targetVariableType.Name.Text, methodInvocationExpression); + parserResult.Error("Unable to process texture coordinates for type [{0}] when processing expression [{1}]", methodInvocationExpression.Span, targetVariableType.Name.Text, methodInvocationExpression); break; } @@ -1587,8 +1647,8 @@ public override Node Visit(ConditionalExpression conditionalExpression) // Convert float4(xxx) ? left : right to mix(left, right, float4(xxx) == 0); if (conditionType is VectorType) { - var methodInvocation = new MethodInvocationExpression("mix", conditionalExpression.Left, conditionalExpression.Right, - new MethodInvocationExpression("equal", conditionalExpression.Condition, new MethodInvocationExpression(new TypeReferenceExpression(conditionType), new LiteralExpression(0)) )); + var methodInvocation = new MethodInvocationExpression("mix", conditionalExpression.Left, conditionalExpression.Right, + new MethodInvocationExpression("equal", conditionalExpression.Condition, new MethodInvocationExpression(new TypeReferenceExpression(conditionType), new LiteralExpression(0)))); return methodInvocation; } else @@ -1675,7 +1735,7 @@ public override Node Visit(ForStatement forStatement) ++breakIndex; var breakVisitor = new BreakContinueVisitor(); var hasBreak = breakVisitor.Run(forStatement, breakFlag, "break", parserResult); - + var continueFlag = new Variable(ScalarType.Bool, "isContinue" + breakIndex, new LiteralExpression(false)); ++breakIndex; var continueVisitor = new BreakContinueVisitor(); @@ -1704,10 +1764,10 @@ public override Node Visit(ForStatement forStatement) var clonedBody = forStatement.Body.DeepClone(); var blockStatement = clonedBody as BlockStatement ?? new BlockStatement(new StatementList(clonedBody)); blockStatement.Statements.Add(new ExpressionStatement(forStatement.Next)); - + if (hasContinue) // reset the flag blockStatement.Statements.Add(new ExpressionStatement(new AssignmentExpression(AssignmentOperator.Default, new VariableReferenceExpression(continueFlag), new LiteralExpression(false)))); - + if (hasBreak) { var ifStatement = new IfStatement(); @@ -1778,7 +1838,7 @@ private static string GetStartForStatement(ForStatement forStatement, out int st } } } - + return null; } @@ -1987,7 +2047,7 @@ public override Node Visit(ArrayInitializerExpression arrayCreationExpression) castToType.Arguments.Add(expression); result = castToType; - } + } } return result; @@ -2148,8 +2208,8 @@ public override Node Visit(BinaryExpression binaryExpression) if (isOperationOnVectors) { parserResult.Error( - "Boolean operation && || on expression [{0}] cannot be converted safely to GLSL, as GLSL doesn't support boolean operators function on a per-component basis. Code is generated but invalid", - binaryExpression.Span, + "Boolean operation && || on expression [{0}] cannot be converted safely to GLSL, as GLSL doesn't support boolean operators function on a per-component basis. Code is generated but invalid", + binaryExpression.Span, binaryExpression); } } @@ -2263,8 +2323,8 @@ public override Node Visit(StatementList statementList) tupleBlock.Statements.Add( new ExpressionStatement( new AssignmentExpression( - AssignmentOperator.Default, - expression, + AssignmentOperator.Default, + expression, new MemberReferenceExpression(new VariableReferenceExpression(TemporaryTupleName), SwizzleMembers.Substring(startMember, argumentDimension))))); startMember += argumentDimension; } @@ -2288,7 +2348,7 @@ public override Node Visit(StatementList statementList) // Handle geometry shader vertex emit if (method != null && method.Target is VariableReferenceExpression) { - var targetVariable = (VariableReferenceExpression) method.Target; + var targetVariable = (VariableReferenceExpression)method.Target; var targetType = targetVariable.TypeInference.TargetType; if (ClassType.IsStreamOutputType(targetType)) { @@ -2302,7 +2362,7 @@ public override Node Visit(StatementList statementList) // streamOutVariable.Qualifiers |= ParameterQualifier.Out; // AddGlobalDeclaration(streamOutVariable); //} - + if (targetType.Name == "TriangleStream") geometryLayoutOutput = "triangle_strip"; else if (targetType.Name == "LineStream") @@ -2317,7 +2377,7 @@ public override Node Visit(StatementList statementList) var returnStatement = ConvertReturn(methodInvocationExpr.Arguments[0], false, null); if (returnStatement is StatementList) - newStatementList.AddRange((StatementList) returnStatement); + newStatementList.AddRange((StatementList)returnStatement); else newStatementList.Add(returnStatement); newStatementList.Add(new ExpressionStatement(new MethodInvocationExpression(new VariableReferenceExpression("EmitVertex")))); @@ -2384,6 +2444,7 @@ public override Node Visit(IndexerExpression indexerExpression) var variableType = variable != null ? variable.Type.ResolveType() : null; var arrayType = variableType as ArrayType; matrixType = variableType as MatrixType; + var classType = variableType as ClassType; if (arrayType != null && arrayType.Dimensions.Count == indices.Count) { @@ -2407,6 +2468,23 @@ public override Node Visit(IndexerExpression indexerExpression) // Return a 1d indexer indexerExpression = new IndexerExpression(targetIterator, finalIndex); } + else if (classType != null && classType.Name.Text.StartsWith("RWTexture")) + { + // Convert assignment to imageLoad, and cast the indexer to an appropriate integer type. + TypeBase indexerType = variableType.Name.Text switch + { + "RWTexture" => ScalarType.Int, + "RWBuffer" => ScalarType.Int, + "RWTexture2D" => VectorType.Int2, + "RWTexture3D" => VectorType.Int3, + _ => throw new NotSupportedException($"imageLoad not supported for {variable.Name.Text}") + }; + + indexerExpression.Target = (Expression)VisitDynamic(indexerExpression.Target); + indexerExpression.Index = (Expression)VisitDynamic(indexerExpression.Index); + + return new MethodInvocationExpression("imageLoad", indexerExpression.Target, new MethodInvocationExpression(new TypeReferenceExpression(indexerType), indexerExpression.Index)); + } } base.Visit(indexerExpression); @@ -2442,7 +2520,7 @@ public override Node Visit(IndexerExpression indexerExpression) return convertRowToColumnMethod; } } - + return indexerExpression; } @@ -2450,9 +2528,9 @@ private void GenerateSamplerMappingAndStrip() { //var samplerMappingVisitor = new SamplerMappingVisitor(samplerMapping); var samplerMappingVisitor = new SamplerMappingVisitor(shader, samplerMapping) - { - TextureFunctionsCompatibilityProfile = TextureFunctionsCompatibilityProfile - }; + { + TextureFunctionsCompatibilityProfile = TextureFunctionsCompatibilityProfile + }; samplerMappingVisitor.Run(entryPoint); // Use the strip visitor in order to remove unused functions/declaration @@ -2537,6 +2615,16 @@ public override Node Visit(Shader shader) variable.Qualifiers.Values.Remove(Ast.Hlsl.StorageQualifier.Static); variable.Qualifiers.Values.Remove(Ast.Hlsl.StorageQualifier.Shared); + if (pipelineStage != PipelineStage.Compute) + { + variable.Qualifiers.Values.Remove(Ast.Hlsl.StorageQualifier.Shared); + } + // groupshared -> shared + else if (variable.Qualifiers.Values.Remove(Ast.Hlsl.StorageQualifier.Groupshared)) + { + variable.Qualifiers.Values.Add(Ast.Hlsl.StorageQualifier.Shared); + } + // If variable is an object type, remove any initial values var type = variable.Type.ResolveType(); if (type is ObjectType) @@ -2604,7 +2692,8 @@ public override Node Visit(Shader shader) entryPoint.Body.Statements.Insert(0, new ExpressionStatement(new AssignmentExpression(AssignmentOperator.Default, new VariableReferenceExpression(localVariable), - localVariable.InitialValue as VariableReferenceExpression) { Span = globalVariable.Span })); + localVariable.InitialValue as VariableReferenceExpression) + { Span = globalVariable.Span })); localVariable.InitialValue = null; shader.Declarations.Insert(indexOfVariable, new DeclarationStatement(localVariable) { Span = globalVariable.Span }); @@ -2646,9 +2735,9 @@ private void TransformInputAndOutputToInterfaceBlock() if (!UseInterfaceForInOut && pipelineStage != PipelineStage.Geometry) return; - var interfaceIn = new Ast.Glsl.InterfaceType(VertexIOInterfaceName) {Qualifiers = Ast.ParameterQualifier.In}; + var interfaceIn = new Ast.Glsl.InterfaceType(VertexIOInterfaceName) { Qualifiers = Ast.ParameterQualifier.In }; - var interfaceOut = new Ast.Glsl.InterfaceType(VertexIOInterfaceName) {Qualifiers = Ast.ParameterQualifier.Out}; + var interfaceOut = new Ast.Glsl.InterfaceType(VertexIOInterfaceName) { Qualifiers = Ast.ParameterQualifier.Out }; var isInAllowed = pipelineStage != PipelineStage.Vertex && pipelineStage != PipelineStage.Geometry; var isOutAllowed = pipelineStage != PipelineStage.Pixel; @@ -2694,7 +2783,7 @@ private void AddGeometryShaderInputDeclaration() // } input[]; // TODO ADD CHECKING - var arrayType = (ArrayType) geometryInputParameter.Type; + var arrayType = (ArrayType)geometryInputParameter.Type; var structType = arrayType.Type.TypeInference.TargetType as StructType; var interfaceType = new Ast.Glsl.InterfaceType { Name = VertexIOInterfaceName }; int location = 0; @@ -2964,7 +3053,8 @@ private static int FindAvailableBinding(bool[] allocatedRegisters, int startingI private static bool IsUniformLike(Variable variable) { return !variable.Qualifiers.Contains(Ast.ParameterQualifier.InOut) && !variable.Qualifiers.Contains(Ast.ParameterQualifier.In) && !variable.Qualifiers.Contains(Ast.ParameterQualifier.Out) - && !variable.Qualifiers.Contains(Ast.Hlsl.StorageQualifier.Static) && !variable.Qualifiers.Contains(Ast.StorageQualifier.Const); + && !variable.Qualifiers.Contains(Ast.Hlsl.StorageQualifier.Static) && !variable.Qualifiers.Contains(Ast.StorageQualifier.Const) + && !variable.Qualifiers.Contains(Ast.StorageQualifier.Shared) && !variable.Qualifiers.Contains(Ast.StorageQualifier.GroupShared); } /// @@ -3334,7 +3424,7 @@ private Expression ConvertReferenceToSemantics(VariableReferenceExpression varRe // if isOutput and structType && not assigntarget if (((isInput || isOutput) && !(type is StructType)) || (isOutput && !isAssignmentTarget)) { - var variable = GetVariableFromSemantic(semantic, type, isInput, varName, span ); + var variable = GetVariableFromSemantic(semantic, type, isInput, varName, span); Variable newVariable; inputAssignment.TryGetValue(variable, out newVariable); @@ -3357,7 +3447,7 @@ private Expression ConvertReferenceToSemantics(VariableReferenceExpression varRe { var variable = FindDeclaration(varRefExpr.Name) as Variable; - if (variable != null) + if (variable != null && !variable.Type.ResolveType().Name.Text.StartsWith("RWTexture") && !variable.Type.ResolveType().Name.Text.StartsWith("RWBuffer")) { Variable newVariable; inputAssignment.TryGetValue(variable, out newVariable); @@ -3408,7 +3498,8 @@ private void ReturnStruct(StructType structType, Expression returnValueExpressio new AssignmentExpression( AssignmentOperator.Default, new VariableReferenceExpression(GetVariableFromSemantic(newSemantic, fieldArrayType, false, fieldRef.FieldNamePath, span).Name), - new IndexerExpression(fieldRef.GetMemberReference(returnValueExpression), new LiteralExpression(i)))) { Span = span }); + new IndexerExpression(fieldRef.GetMemberReference(returnValueExpression), new LiteralExpression(i)))) + { Span = span }); } } @@ -3428,7 +3519,8 @@ private void ReturnStruct(StructType structType, Expression returnValueExpressio statementList.Add( new ExpressionStatement( new AssignmentExpression( - AssignmentOperator.Default, new VariableReferenceExpression(semanticVariable.Name) { TypeInference = { Declaration = semanticVariable } }, semanticValue)) { Span = returnValueExpression.Span }); + AssignmentOperator.Default, new VariableReferenceExpression(semanticVariable.Name) { TypeInference = { Declaration = semanticVariable } }, semanticValue)) + { Span = returnValueExpression.Span }); } } } @@ -3501,7 +3593,8 @@ private Statement ConvertReturn(Expression returnValueExpression, bool emitRetur new AssignmentExpression( AssignmentOperator.Default, new VariableReferenceExpression(GetVariableFromSemantic(semantic, CurrentFunction.ReturnType.ResolveType(), false, null, semantic.Span).Name), - returnValueExpression)) { Span = span.Value } ); + returnValueExpression)) + { Span = span.Value }); } } @@ -3852,7 +3945,7 @@ private TagLayout GetTagLayout(Node node, string alias = null) } // Use output or input name - layoutTag.Name = variable.Qualifiers.Contains(Ast.ParameterQualifier.Out) ? variableLayoutRule.NameOutput : variableLayoutRule.Name; + layoutTag.Name = variable.Qualifiers.Contains(Ast.ParameterQualifier.Out) ? variableLayoutRule.NameOutput : variableLayoutRule.Name; } } else if (constantBuffer != null) @@ -3876,15 +3969,10 @@ private TagLayout GetTagLayout(Node node, string alias = null) } } - if (variable != null) - { - - } - if (variable != null && layoutTag.Name == null) layoutTag.Name = variable.Name.Text; - layoutTag.Qualifier = new Ast.Glsl.LayoutQualifier(); + layoutTag.Qualifier = new LayoutQualifier(); } return layoutTag; @@ -3896,7 +3984,7 @@ private TagLayout GetTagLayout(Node node, string alias = null) private void RebindVariableReferenceExpressions() { SearchVisitor.Run( - shader, + shader, node => { if (node is VariableReferenceExpression) @@ -3924,7 +4012,7 @@ private void RebindVariableReferenceExpressions() private void RemoveDefaultParametersForMethods() { SearchVisitor.Run( - shader, + shader, node => { var declaration = node as Parameter; @@ -3991,7 +4079,7 @@ private void RemapCoordinates(StatementList list) private void RenameGlslKeywords() { SearchVisitor.Run( - shader, + shader, node => { var declaration = node as IDeclaration; @@ -4020,7 +4108,7 @@ private KeyValuePair GetGlVariableFromSemantic(Semantic rawSemantic var semanticMapping = isInput ? builtinInputs : builtinOutputs; semanticGl = null; - + if (semanticMapping != null && !semanticMapping.TryGetValue(semanticName, out semanticGl)) semanticMapping.TryGetValue(semantic.Key, out semanticGl); @@ -4066,7 +4154,7 @@ private Semantic ResolveSemantic(Semantic rawSemantic, TypeBase type, bool isInp { string semanticGlBase = null; string semanticGl = null; - var semantic = GetGlVariableFromSemantic(rawSemantic, isInput, out semanticGl, out semanticGlBase, out semanticIndex); + var semantic = GetGlVariableFromSemantic(rawSemantic, isInput, out semanticGl, out semanticGlBase, out semanticIndex); if (semanticGl == null) { @@ -4116,7 +4204,7 @@ private Semantic ResolveSemantic(Semantic rawSemantic, TypeBase type, bool isInp else { parserResult.Warning("No default type defined for glsl semantic [{0}]. Use [{1}] implicit type instead.", rawSemantic.Span, semanticGlBase, type); - glslType = type; + glslType = type; } } @@ -4179,7 +4267,7 @@ private void TranformToGlslTypes() // Replace all generic shader types to their glsl equivalent. SearchVisitor.Run( - shader, + shader, node => { if (node is TypeBase && !(node is Typedef) && !(node is ArrayType)) @@ -4208,6 +4296,10 @@ private TypeBase ConvertType(TypeBase targetType) if (targetTypeName.StartsWith("Texture", StringComparison.Ordinal)) targetTypeName = "texture" + targetTypeName["Texture".Length..]; + else if (targetTypeName.StartsWith("RWTexture", StringComparison.Ordinal)) + targetTypeName = "image" + targetTypeName["RWTexture".Length..]; + else if (targetTypeName.StartsWith("RWBuffer", StringComparison.Ordinal)) + targetTypeName = "imageBuffer"; else if (targetTypeName.StartsWith("Buffer", StringComparison.Ordinal)) targetTypeName = "textureBuffer"; else return null; @@ -4419,7 +4511,7 @@ private void TransformGlobalMultipleVariableToSingleVariable() } - private static List GetMembers(StructType structType, List members = null, List fieldStack = null ) + private static List GetMembers(StructType structType, List members = null, List fieldStack = null) { // Cache the members if they have been already calculated for a particular type // Though, this is not realy efficient (should cache nested struct member reference...) @@ -4456,7 +4548,7 @@ private static List GetMembers(StructType structType, Lis var fieldPath = new StringBuilder(); bool isFirst = true; - foreach(var parentField in Enumerable.Reverse(fieldStack)) + foreach (var parentField in Enumerable.Reverse(fieldStack)) { if (!isFirst) fieldPath.Append("_"); @@ -4520,7 +4612,7 @@ public MemberReferenceExpression GetMemberReference(Expression target) foreach (var parentField in Enumerable.Reverse(ParentFields)) { - currentMemberRef.Target = new MemberReferenceExpression(currentMemberRef.Target, parentField.Name); + currentMemberRef.Target = new MemberReferenceExpression(currentMemberRef.Target, parentField.Name); } return currentMemberRef; } diff --git a/sources/shaders/Stride.Core.Shaders/Convertor/HlslToGlslWriter.cs b/sources/shaders/Stride.Core.Shaders/Convertor/HlslToGlslWriter.cs index 38187bbc06..f0bf82160c 100644 --- a/sources/shaders/Stride.Core.Shaders/Convertor/HlslToGlslWriter.cs +++ b/sources/shaders/Stride.Core.Shaders/Convertor/HlslToGlslWriter.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; +using System.Collections.Generic; using System.Globalization; using System.Linq; using Stride.Core.Shaders.Ast; @@ -55,6 +56,8 @@ public HlslToGlslWriter(GlslShaderPlatform shaderPlatform, int shaderVersion, Pi public string ExtraHeaders { get; set; } + public List Extensions { get; } = []; + #region Public Methods /// @@ -69,6 +72,12 @@ public override void Visit(Shader shader) Write(" es"); WriteLine(); + + foreach (var extension in Extensions) + { + WriteLine($"#extension {extension} : enable"); + } + WriteLine(); if (shaderPlatform == GlslShaderPlatform.OpenGLES) @@ -244,8 +253,22 @@ public override void Visit(Typedef typedef) /// public override void Visit(AttributeDeclaration attributeDeclaration) { - - } + if (pipelineStage == PipelineStage.Compute && attributeDeclaration.Name == "numthreads") + { + if (attributeDeclaration.Parameters.Count == 1) + { + WriteLine($"layout(local_size_x={attributeDeclaration.Parameters[0].Value}) in;"); + } + else if (attributeDeclaration.Parameters.Count == 2) + { + WriteLine($"layout(local_size_x={attributeDeclaration.Parameters[0].Value}, local_size_y={attributeDeclaration.Parameters[1].Value}) in;"); + } + else if (attributeDeclaration.Parameters.Count == 3) + { + WriteLine($"layout(local_size_x={attributeDeclaration.Parameters[0].Value}, local_size_y={attributeDeclaration.Parameters[1].Value}, local_size_z={attributeDeclaration.Parameters[2].Value}) in;"); + } + } + } /// public override void Visit(CastExpression castExpression) From 19ecc1d6f760dd4a5888d0ed40a4900e8b176a7e Mon Sep 17 00:00:00 2001 From: Johan Gustafsson Date: Sat, 22 Mar 2025 15:47:29 +0100 Subject: [PATCH 2/7] Using GL_EXT_samplerless_texture_functions instead of NoSampler --- .../engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs | 2 +- .../Stride.Shaders.Compiler/OpenGL/ShaderCompiler.cs | 9 ++------- .../Stride.Core.Shaders/Convertor/HlslToGlslConvertor.cs | 6 +----- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs b/sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs index f2784c4040..47f697921d 100644 --- a/sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs +++ b/sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs @@ -460,7 +460,7 @@ public unsafe void ResourceBarrierTransition(GraphicsResource resource, Graphics case GraphicsResourceState.PixelShaderResource: texture.NativeLayout = VkImageLayout.ShaderReadOnlyOptimal; texture.NativeAccessMask = VkAccessFlags.ShaderRead; - texture.NativePipelineStageMask = VkPipelineStageFlags.FragmentShader | VkPipelineStageFlags.ComputeShader; // TODO: Not sure why I did this can probably double check ... + texture.NativePipelineStageMask = VkPipelineStageFlags.FragmentShader | VkPipelineStageFlags.ComputeShader; break; case GraphicsResourceState.GenericRead: texture.NativeLayout = VkImageLayout.General; diff --git a/sources/engine/Stride.Shaders.Compiler/OpenGL/ShaderCompiler.cs b/sources/engine/Stride.Shaders.Compiler/OpenGL/ShaderCompiler.cs index d84dc39dd3..c9d3c8af35 100644 --- a/sources/engine/Stride.Shaders.Compiler/OpenGL/ShaderCompiler.cs +++ b/sources/engine/Stride.Shaders.Compiler/OpenGL/ShaderCompiler.cs @@ -295,13 +295,6 @@ private string Compile(string shaderSource, string entryPoint, ShaderStage stage if (shaderPlatform == GlslShaderPlatform.Vulkan) { - // Register "NoSampler", required by HLSL=>GLSL translation to support HLSL such as texture.Load(). - var noSampler = new EffectResourceBindingDescription { KeyInfo = { KeyName = "NoSampler" }, RawName = "NoSampler", Class = EffectParameterClass.Sampler, SlotStart = -1, SlotCount = 1 }; - reflection.ResourceBindings.Add(noSampler); - - // Make sure it's a point sampler as some texture formats do not support linear sampling which will result in validation errors. - reflection.SamplerStates.Add(new EffectSamplerStateBinding("NoSampler", new SamplerStateDescription(TextureFilter.Point, TextureAddressMode.Clamp))); - // Defines the ordering of resource groups in Vulkan. This is mirrored in the PipelineState var resourceGroups = reflection.ResourceBindings.Select(x => x.ResourceGroup ?? "Globals").Distinct().ToList(); @@ -370,6 +363,8 @@ private string Compile(string shaderSource, string entryPoint, ShaderStage stage glslShaderWriter.Extensions.Add("GL_EXT_shader_image_load_formatted"); } + glslShaderWriter.Extensions.Add("GL_EXT_samplerless_texture_functions"); + // Write shader glslShaderWriter.Visit(glslShader); diff --git a/sources/shaders/Stride.Core.Shaders/Convertor/HlslToGlslConvertor.cs b/sources/shaders/Stride.Core.Shaders/Convertor/HlslToGlslConvertor.cs index 0f2653339f..abcde6014e 100644 --- a/sources/shaders/Stride.Core.Shaders/Convertor/HlslToGlslConvertor.cs +++ b/sources/shaders/Stride.Core.Shaders/Convertor/HlslToGlslConvertor.cs @@ -2549,10 +2549,6 @@ private void GenerateSamplerMappingAndStrip() AddGlobalDeclaration(textureSampler.Value); } } - else - { - AddGlobalDeclaration(new Variable(StateType.SamplerState, "NoSampler")); - } } /// @@ -3751,7 +3747,7 @@ private Expression GetGLSampler(Variable sampler, Variable texture, bool forceNu } else { - return new MethodInvocationExpression(new TypeReferenceExpression(glslSampler.Type), new VariableReferenceExpression(texture), new VariableReferenceExpression("NoSampler")); + return new VariableReferenceExpression(texture); } } From 7748620af310ce17c07cadb93946c6bdddfbb8b7 Mon Sep 17 00:00:00 2001 From: Johan Gustafsson Date: Thu, 12 Jun 2025 13:42:45 +0200 Subject: [PATCH 3/7] fixed compilation error and checking descriptor pool type coutns --- .../Vulkan/GraphicsDevice.Vulkan.cs | 34 +++++++++++++------ sources/targets/Stride.props | 2 +- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs b/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs index 4be30324f8..3f5baf538f 100644 --- a/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs +++ b/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs @@ -48,20 +48,20 @@ public partial class GraphicsDevice internal HeapPool DescriptorPools; internal const uint MaxDescriptorSetCount = 256; - internal readonly uint[] MaxDescriptorTypeCounts = new uint[DescriptorSetLayout.DescriptorTypeCount] - { + internal uint[] MaxDescriptorTypeCounts = + [ 256, // Sampler 0, // CombinedImageSampler 512, // SampledImage - 32, // StorageImage + 64, // StorageImage 64, // UniformTexelBuffer - 32, // StorageTexelBuffer + 64, // StorageTexelBuffer 512, // UniformBuffer 0, // StorageBuffer 0, // UniformBufferDynamic 0, // StorageBufferDynamic 0 // InputAttachment - }; + ]; internal Buffer EmptyTexelBufferInt, EmptyTexelBufferFloat; internal Texture EmptyTexture; @@ -264,6 +264,22 @@ private unsafe void InitializePlatformDevice(GraphicsProfile[] graphicsProfiles, ConstantBufferDataPlacementAlignment = (int)physicalDeviceProperties.limits.minUniformBufferOffsetAlignment; TimestampFrequency = (long)(1.0e9 / physicalDeviceProperties.limits.timestampPeriod); // Resolution in nanoseconds + // Configure descriptor type max counts + void SetMaxDescriptorTypeCount(VkDescriptorType type, uint limit) + => MaxDescriptorTypeCounts[(int)type] = Math.Min(MaxDescriptorTypeCounts[(int)type], limit); + + SetMaxDescriptorTypeCount(VkDescriptorType.Sampler, physicalDeviceProperties.limits.maxDescriptorSetSamplers); + SetMaxDescriptorTypeCount(VkDescriptorType.CombinedImageSampler, 0); // Not defined. + SetMaxDescriptorTypeCount(VkDescriptorType.SampledImage, physicalDeviceProperties.limits.maxDescriptorSetSampledImages); + SetMaxDescriptorTypeCount(VkDescriptorType.StorageImage, physicalDeviceProperties.limits.maxDescriptorSetStorageImages); + SetMaxDescriptorTypeCount(VkDescriptorType.UniformTexelBuffer, physicalDeviceProperties.limits.maxDescriptorSetSampledImages); // No individual limit + SetMaxDescriptorTypeCount(VkDescriptorType.StorageTexelBuffer, physicalDeviceProperties.limits.maxDescriptorSetStorageImages); // No individual limit + SetMaxDescriptorTypeCount(VkDescriptorType.UniformBuffer, physicalDeviceProperties.limits.maxDescriptorSetUniformBuffers); + SetMaxDescriptorTypeCount(VkDescriptorType.StorageBuffer, physicalDeviceProperties.limits.maxDescriptorSetStorageBuffers); + SetMaxDescriptorTypeCount(VkDescriptorType.UniformBufferDynamic, physicalDeviceProperties.limits.maxDescriptorSetUniformBuffersDynamic); + SetMaxDescriptorTypeCount(VkDescriptorType.StorageBufferDynamic, physicalDeviceProperties.limits.maxDescriptorSetStorageBuffersDynamic); + SetMaxDescriptorTypeCount(VkDescriptorType.InputAttachment, physicalDeviceProperties.limits.maxDescriptorSetInputAttachments); + RequestedProfile = graphicsProfiles.First(); var queueProperties = vkGetPhysicalDeviceQueueFamilyProperties(NativePhysicalDevice); @@ -298,16 +314,12 @@ private unsafe void InitializePlatformDevice(GraphicsProfile[] graphicsProfiles, enabledFeature.shaderStorageImageWriteWithoutFormat = true; } - var extensionProperties = vkEnumerateDeviceExtensionProperties(NativePhysicalDevice); - var availableExtensionNames = new List(); - var desiredExtensionNames = new List(); - - fixed (VkExtensionProperties* extensionPropertiesPtr = extensionProperties) + Span supportedExtensionProperties = stackalloc VkUtf8String[] { VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_EXT_DEBUG_MARKER_EXTENSION_NAME, }; - Span supportedExtensionProperties = stackalloc VkUtf8String[] + var availableExtensionProperties = GetAvailableExtensionProperties(supportedExtensionProperties); ValidateExtensionPropertiesAvailability(availableExtensionProperties); var desiredExtensionProperties = new HashSet diff --git a/sources/targets/Stride.props b/sources/targets/Stride.props index c02a237678..2e38fae783 100644 --- a/sources/targets/Stride.props +++ b/sources/targets/Stride.props @@ -15,7 +15,7 @@ - + Vulkan Direct3D11;Direct3D12;OpenGL;OpenGLES;Vulkan From c37f53ccce370aeec0c8206ce56bb616e3437d2f Mon Sep 17 00:00:00 2001 From: Johan Gustafsson Date: Thu, 12 Jun 2025 13:51:08 +0200 Subject: [PATCH 4/7] checking for shaderStorageImageReadWithoutFormat / shaderStorageImageWriteWithoutFormat support --- .../Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs b/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs index 3f5baf538f..70a51c27c1 100644 --- a/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs +++ b/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs @@ -308,9 +308,15 @@ void SetMaxDescriptorTypeCount(VkDescriptorType type, uint limit) depthClamp = true, }; - if (graphicsProfiles.Any(x => x >= GraphicsProfile.Level_11_0)) + vkGetPhysicalDeviceFeatures(NativePhysicalDevice, out var deviceFeatures); + + if (deviceFeatures.shaderStorageImageReadWithoutFormat) { enabledFeature.shaderStorageImageReadWithoutFormat = true; + } + + if (deviceFeatures.shaderStorageImageWriteWithoutFormat) + { enabledFeature.shaderStorageImageWriteWithoutFormat = true; } From a8eb1556a44ba4ae7bc0aa85fbcf7dea4c29dbab Mon Sep 17 00:00:00 2001 From: Johan Gustafsson Date: Fri, 4 Apr 2025 09:13:12 +0200 Subject: [PATCH 5/7] various tweaks to get vulkan to work with stride streaming terrain --- .../Stride.Graphics/Vulkan/Buffer.Vulkan.cs | 12 +++ .../Vulkan/CommandList.Vulkan.cs | 15 ++- .../Vulkan/GraphicsDevice.Vulkan.cs | 2 +- .../Stride.Graphics/Vulkan/Texture.Vulkan.cs | 2 +- .../Vulkan/VulkanConvertExtensions.cs | 16 +++ .../OpenGL/ShaderCompiler.cs | 6 ++ .../Ast/StorageQualifier.cs | 5 + .../Convertor/HlslToGlslConvertor.cs | 101 ++++++++++++++++-- .../Convertor/SamplerMappingVisitor.cs | 32 +++++- .../Stride.NuGetResolver.Targets.projitems | 2 +- 10 files changed, 178 insertions(+), 15 deletions(-) diff --git a/sources/engine/Stride.Graphics/Vulkan/Buffer.Vulkan.cs b/sources/engine/Stride.Graphics/Vulkan/Buffer.Vulkan.cs index a532eba672..f23d2a8e21 100644 --- a/sources/engine/Stride.Graphics/Vulkan/Buffer.Vulkan.cs +++ b/sources/engine/Stride.Graphics/Vulkan/Buffer.Vulkan.cs @@ -126,6 +126,18 @@ public unsafe void Recreate(IntPtr dataPointer) NativePipelineStageMask |= VkPipelineStageFlags.VertexShader | VkPipelineStageFlags.FragmentShader; } + if ((ViewFlags & BufferFlags.StructuredBuffer) != 0) + { + createInfo.usage |= VkBufferUsageFlags.StorageBuffer; + NativeAccessMask |= VkAccessFlags.UniformRead; + NativePipelineStageMask |= VkPipelineStageFlags.VertexShader | VkPipelineStageFlags.FragmentShader; + + if ((ViewFlags & BufferFlags.UnorderedAccess) != 0) + { + NativeAccessMask |= VkAccessFlags.ShaderWrite; + } + } + if ((ViewFlags & BufferFlags.ShaderResource) != 0) { createInfo.usage |= VkBufferUsageFlags.UniformTexelBuffer; diff --git a/sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs b/sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs index 47f697921d..0dfb7f7d15 100644 --- a/sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs +++ b/sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs @@ -373,6 +373,12 @@ private unsafe void BindDescriptorSets() write->pTexelBufferView = &descriptorData->BufferView; break; + case VkDescriptorType.StorageBuffer: + buffer = heapObject.Value as Buffer; + descriptorData->BufferInfo = new VkDescriptorBufferInfo { buffer = buffer?.NativeBuffer ?? VkBuffer.Null, offset = (ulong)heapObject.Offset, range = (ulong)(buffer?.SizeInBytes ?? 0)}; + write->pBufferInfo = &descriptorData->BufferInfo; + break; + default: throw new InvalidOperationException(); } @@ -1321,10 +1327,11 @@ public unsafe MappedResource MapSubresource(GraphicsResource resource, int subRe throw new InvalidOperationException(); } - if (mapMode == MapMode.WriteDiscard) - { - throw new InvalidOperationException("Can't use WriteDiscard on Graphics API that doesn't support renaming"); - } + // Maybe it just works if removed? + //if (mapMode == MapMode.WriteDiscard) + //{ + // throw new InvalidOperationException("Can't use WriteDiscard on Graphics API that doesn't support renaming"); + //} if (mapMode != MapMode.WriteNoOverwrite && mapMode != MapMode.Write) { diff --git a/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs b/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs index 70a51c27c1..f09de808ff 100644 --- a/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs +++ b/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs @@ -57,7 +57,7 @@ public partial class GraphicsDevice 64, // UniformTexelBuffer 64, // StorageTexelBuffer 512, // UniformBuffer - 0, // StorageBuffer + 64, // StorageBuffer 0, // UniformBufferDynamic 0, // StorageBufferDynamic 0 // InputAttachment diff --git a/sources/engine/Stride.Graphics/Vulkan/Texture.Vulkan.cs b/sources/engine/Stride.Graphics/Vulkan/Texture.Vulkan.cs index c138ce2872..f1ac38d928 100644 --- a/sources/engine/Stride.Graphics/Vulkan/Texture.Vulkan.cs +++ b/sources/engine/Stride.Graphics/Vulkan/Texture.Vulkan.cs @@ -487,7 +487,7 @@ private unsafe VkImageView GetImageView(ViewType viewType, int arrayOrDepthSlice if (!IsShaderResource) return VkImageView.Null; - if (viewType == ViewType.MipBand) + if (viewType == ViewType.MipBand && IsRenderTarget) throw new NotSupportedException("ViewSlice.MipBand is not supported for render targets"); GetViewSliceBounds(viewType, ref arrayOrDepthSlice, ref mipIndex, out var arrayOrDepthCount, out var mipCount); diff --git a/sources/engine/Stride.Graphics/Vulkan/VulkanConvertExtensions.cs b/sources/engine/Stride.Graphics/Vulkan/VulkanConvertExtensions.cs index 78989269c9..45cc5ade2b 100644 --- a/sources/engine/Stride.Graphics/Vulkan/VulkanConvertExtensions.cs +++ b/sources/engine/Stride.Graphics/Vulkan/VulkanConvertExtensions.cs @@ -276,6 +276,15 @@ public static void ConvertPixelFormat(PixelFormat inputFormat, out VkFormat form pixelSize = 4; break; + case PixelFormat.R10G10B10A2_UInt: + format = VkFormat.A2R10G10B10UIntPack32; + pixelSize = 4; + break; + case PixelFormat.R10G10B10A2_UNorm: + format = VkFormat.A2R10G10B10UNormPack32; + pixelSize = 4; + break; + case PixelFormat.R16_Float: format = VkFormat.R16Sfloat; pixelSize = 2; @@ -396,6 +405,10 @@ public static void ConvertPixelFormat(PixelFormat inputFormat, out VkFormat form format = VkFormat.D32Sfloat; pixelSize = 4; break; + case PixelFormat.D32_Float_S8X24_UInt: + format = VkFormat.D32SFloatS8UInt; + pixelSize = 8; + break; case PixelFormat.ETC1: case PixelFormat.ETC2_RGB: // ETC1 upper compatible @@ -554,6 +567,8 @@ public static VkDescriptorType ConvertDescriptorType(EffectParameterClass @class case EffectParameterType.Buffer: return VkDescriptorType.UniformTexelBuffer; + case EffectParameterType.StructuredBuffer: + return VkDescriptorType.StorageBuffer; default: throw new NotImplementedException(); @@ -578,6 +593,7 @@ public static VkDescriptorType ConvertDescriptorType(EffectParameterClass @class return VkDescriptorType.StorageImage; case EffectParameterType.Buffer: + case EffectParameterType.StructuredBuffer: return VkDescriptorType.StorageBuffer; default: diff --git a/sources/engine/Stride.Shaders.Compiler/OpenGL/ShaderCompiler.cs b/sources/engine/Stride.Shaders.Compiler/OpenGL/ShaderCompiler.cs index c9d3c8af35..85c4aeaee1 100644 --- a/sources/engine/Stride.Shaders.Compiler/OpenGL/ShaderCompiler.cs +++ b/sources/engine/Stride.Shaders.Compiler/OpenGL/ShaderCompiler.cs @@ -344,6 +344,12 @@ private string Compile(string shaderSource, string entryPoint, ShaderStage stage layoutQualifier.Layouts.Add(new LayoutKeyValue("binding", layoutBindingIndex + 1)); resourceBindings.Add(bindings[layoutBindingIndex].Key.KeyName, layoutBindingIndex + 1); + + // Buffer should not be marked with uniform, this probably should not be here but it works and does not mess anything up. + if (variable.Type.Qualifiers.Contains(StorageQualifier.Buffer)) + { + variable.Qualifiers.Values.Remove(StorageQualifier.Uniform); + } } } } diff --git a/sources/shaders/Stride.Core.Shaders/Ast/StorageQualifier.cs b/sources/shaders/Stride.Core.Shaders/Ast/StorageQualifier.cs index 9850ee3964..7fc637b84a 100644 --- a/sources/shaders/Stride.Core.Shaders/Ast/StorageQualifier.cs +++ b/sources/shaders/Stride.Core.Shaders/Ast/StorageQualifier.cs @@ -21,6 +21,11 @@ public partial class StorageQualifier /// public static readonly Qualifier Uniform = new Qualifier("uniform"); + /// + /// Uniform qualifier. + /// + public static readonly Qualifier Buffer = new Qualifier("buffer"); + /// /// Shared qualifier. /// diff --git a/sources/shaders/Stride.Core.Shaders/Convertor/HlslToGlslConvertor.cs b/sources/shaders/Stride.Core.Shaders/Convertor/HlslToGlslConvertor.cs index abcde6014e..0798141054 100644 --- a/sources/shaders/Stride.Core.Shaders/Convertor/HlslToGlslConvertor.cs +++ b/sources/shaders/Stride.Core.Shaders/Convertor/HlslToGlslConvertor.cs @@ -101,6 +101,8 @@ public class HlslToGlslConvertor : ShaderRewriter private int breakIndex = 0; + private int structedBufferCounter = 0; + #endregion #region Constructors and Destructors @@ -243,7 +245,7 @@ public HlslToGlslConvertor(GlslShaderPlatform shaderPlatform, int shaderVersion, { "GroupMemoryBarrier", "groupMemoryBarrier" }, //{ "D3DCOLORtoUBYTE4", "ivec4" }, }; -} + } #endregion @@ -1093,7 +1095,7 @@ protected Statement VisitStatementAsMemberInvocation(Statement statement, Method if (memberReferenceExpression.Member == "GetDimensions") { var textureRef = memberReferenceExpression.Target as VariableReferenceExpression; - var variableTexture = this.FindGlobalVariableFromExpression(textureRef); + var variableTexture = FindParameterOrGlobalVariableFromExpression(textureRef); if (variableTexture == null) { @@ -1233,6 +1235,7 @@ protected Statement VisitStatementAsAssignExpression(Statement statement, Assign "RWBuffer" => ScalarType.Int, "RWTexture2D" => VectorType.Int2, "RWTexture3D" => VectorType.Int3, + "RWTexture2DArray" => VectorType.Int3, _ => throw new NotSupportedException($"imageStore not supported for {variable.Name.Text}") }; @@ -1307,6 +1310,24 @@ public override Node Visit(MethodInvocationExpression methodInvocationExpression return new ParenthesizedExpression(new BinaryExpression(BinaryOperator.Multiply, leftParameter, rightParameter)); } + if (methodName == "rcp") + { + var rightParameter = ConvertToSafeExpressionForBinary(methodInvocationExpression.Arguments[0]); + return new ParenthesizedExpression(new BinaryExpression(BinaryOperator.Divide, new LiteralExpression(1.0f), rightParameter)); + } + + if (methodName == "mad") + { + var firstParameter = ConvertToSafeExpressionForBinary(methodInvocationExpression.Arguments[0]); + var secondParameter = ConvertToSafeExpressionForBinary(methodInvocationExpression.Arguments[1]); + var thirdParameter = ConvertToSafeExpressionForBinary(methodInvocationExpression.Arguments[2]); + + var multiply = new BinaryExpression(BinaryOperator.Multiply, firstParameter, secondParameter); + var add = new BinaryExpression(BinaryOperator.Plus, multiply, thirdParameter); + + return new ParenthesizedExpression(add); + } + if (methodName == "lit") { // http://msdn.microsoft.com/en-us/library/bb509619%28v=vs.85%29.aspx @@ -1389,7 +1410,7 @@ public override Node Visit(MethodInvocationExpression methodInvocationExpression var memberReferenceExpression = methodInvocationExpression.Target as MemberReferenceExpression; if (memberReferenceExpression != null) { - var targetVariable = FindGlobalVariableFromExpression(memberReferenceExpression.Target); + var targetVariable = FindParameterOrGlobalVariableFromExpression(memberReferenceExpression.Target); if (targetVariable == null) { parserResult.Error("Unable to find target variable for expression [{0}]", methodInvocationExpression.Span, methodInvocationExpression); @@ -1428,7 +1449,7 @@ public override Node Visit(MethodInvocationExpression methodInvocationExpression // texture.Load() doesn't require a sampler if (!isLoad) { - sampler = this.FindGlobalVariableFromExpression(methodInvocationExpression.Arguments[0]); + sampler = FindParameterOrGlobalVariableFromExpression(methodInvocationExpression.Arguments[0]); } var glslSampler = GetGLSampler(sampler, targetVariable, true); @@ -2477,6 +2498,7 @@ public override Node Visit(IndexerExpression indexerExpression) "RWBuffer" => ScalarType.Int, "RWTexture2D" => VectorType.Int2, "RWTexture3D" => VectorType.Int3, + "RWTexture2DArray" => VectorType.Int3, _ => throw new NotSupportedException($"imageLoad not supported for {variable.Name.Text}") }; @@ -2485,6 +2507,16 @@ public override Node Visit(IndexerExpression indexerExpression) return new MethodInvocationExpression("imageLoad", indexerExpression.Target, new MethodInvocationExpression(new TypeReferenceExpression(indexerType), indexerExpression.Index)); } + else if (classType != null && (classType.Name.Text.StartsWith("StructuredBuffer") || classType.Name.Text.StartsWith("RWStructuredBuffer"))) + { + // Convert to TargetName.Buffer[index] + indexerExpression.Target = (Expression)VisitDynamic(indexerExpression.Target); + indexerExpression.Index = (Expression)VisitDynamic(indexerExpression.Index); + + indexerExpression.Target = new MemberReferenceExpression(indexerExpression.Target, "Buffer"); + + return indexerExpression; + } } base.Visit(indexerExpression); @@ -3683,6 +3715,24 @@ private void FlattenArrayCreationExpression(ArrayInitializerExpression from, Lis } } + private Variable FindParameterOrGlobalVariableFromExpression(Expression expression) + { + var variableRef = expression as VariableReferenceExpression; + if (variableRef != null) + { + // Check if present in parameter list first. + var parameter = CurrentFunction.Parameters.FirstOrDefault(x => x is Stride.Core.Shaders.Ast.Parameter param && param.Name == variableRef.Name); + if (parameter != null) + { + return parameter; + } + + return FindGlobalVariableFromExpression(expression); + } + + return null; + } + private Variable FindGlobalVariableFromExpression(Expression expression) { var variableRef = expression as VariableReferenceExpression; @@ -3695,7 +3745,7 @@ private Variable FindGlobalVariableFromExpression(Expression expression) // If a variable has an initial value, find the global variable if (!shader.Declarations.Contains(variable) && variable.InitialValue != null) { - return this.FindGlobalVariableFromExpression(variable.InitialValue); + return FindGlobalVariableFromExpression(variable.InitialValue); } // Is this a global variable? @@ -4290,6 +4340,29 @@ private TypeBase ConvertType(TypeBase targetType) { var targetTypeName = targetType.Name.Text; + if ((targetTypeName.Equals("StructuredBuffer", StringComparison.Ordinal) || targetTypeName.Equals("RWStructuredBuffer", StringComparison.Ordinal)) + && targetType is IGenerics structuredBufferGenericType) + { + // Convert to "readonly buffer Name { GenericType Buffer; }; + var structuredBufferType = structuredBufferGenericType.GenericArguments[0]; + + var name = $"Stride_Internal_{structuredBufferType.Name.Text}_{++structedBufferCounter}"; + var bufferType = new Ast.Glsl.InterfaceType(name) + { + Qualifiers = Qualifier.None, // Assign buffer qualifier later as we want the ordering to be correct (readonly should come first) + Fields = [new Variable(new ArrayType(structuredBufferType, new EmptyExpression()), "Buffer")] + }; + + if (targetTypeName.Equals("StructuredBuffer", StringComparison.Ordinal)) + { + bufferType.Qualifiers |= StorageQualifier.ReadOnly; + } + + bufferType.Qualifiers |= StorageQualifier.Buffer; + + return bufferType; + } + if (targetTypeName.StartsWith("Texture", StringComparison.Ordinal)) targetTypeName = "texture" + targetTypeName["Texture".Length..]; else if (targetTypeName.StartsWith("RWTexture", StringComparison.Ordinal)) @@ -4584,18 +4657,32 @@ private void ReorderVariableQualifiers() } } + /// + /// Apply std140 layout to all constant and storage buffers. + /// private void ApplyStd140Layout() { foreach (var constantBuffer in shader.Declarations.OfType()) { - var layoutQualifier = constantBuffer.Qualifiers.OfType().FirstOrDefault(); + var layoutQualifier = constantBuffer.Qualifiers.OfType().FirstOrDefault(); if (layoutQualifier == null) { - layoutQualifier = new Stride.Core.Shaders.Ast.Glsl.LayoutQualifier(); + layoutQualifier = new LayoutQualifier(); constantBuffer.Qualifiers |= layoutQualifier; } layoutQualifier.Layouts.Add(new LayoutKeyValue("std140")); } + + foreach (var variable in shader.Declarations.OfType().Where(x => x.Type.Qualifiers.Contains(StorageQualifier.Buffer))) + { + var layoutQualifier = variable.Qualifiers.OfType().FirstOrDefault(); + if (layoutQualifier == null) + { + layoutQualifier = new LayoutQualifier(); + variable.Qualifiers |= layoutQualifier; + } + layoutQualifier.Layouts.Add(new LayoutKeyValue("std430")); // But this is not std140? You are very much correct. + } } private class StructMemberReference diff --git a/sources/shaders/Stride.Core.Shaders/Convertor/SamplerMappingVisitor.cs b/sources/shaders/Stride.Core.Shaders/Convertor/SamplerMappingVisitor.cs index 15b4cdd0bc..df60a525b8 100644 --- a/sources/shaders/Stride.Core.Shaders/Convertor/SamplerMappingVisitor.cs +++ b/sources/shaders/Stride.Core.Shaders/Convertor/SamplerMappingVisitor.cs @@ -64,6 +64,36 @@ public override void Run(MethodDefinition methodEntry) } } + private Variable FindParameterOrGlobalVariable(Expression expression) + { + var variableRef = expression as VariableReferenceExpression; + if (variableRef != null) + { + // Check if present in parameter list first. + MethodDeclaration currentFunction = null; + for (var i = NodeStack.Count - 1; i >= 0; i--) + { + if (NodeStack[i] is MethodDeclaration function) + { + currentFunction = function; + break; + } + } + if (currentFunction != null) + { + var parameter = currentFunction.Parameters.FirstOrDefault(x => x is Stride.Core.Shaders.Ast.Parameter param && param.Name == variableRef.Name); + if (parameter != null) + { + return parameter; + } + } + + return FindGlobalVariable(expression); + } + + return null; + } + private Variable FindGlobalVariable(Expression expression) { var variableRef = expression as VariableReferenceExpression; @@ -108,7 +138,7 @@ public override Node Visit(MethodInvocationExpression methodInvocationExpression if (memberRef != null) { // TODO handle Texture2D - var textureVariable = this.FindGlobalVariable(memberRef.Target); + var textureVariable = FindParameterOrGlobalVariable(memberRef.Target); if (textureVariable != null) { diff --git a/sources/shared/Stride.NuGetResolver.Targets/Stride.NuGetResolver.Targets.projitems b/sources/shared/Stride.NuGetResolver.Targets/Stride.NuGetResolver.Targets.projitems index 6a55e67415..73c84ac324 100644 --- a/sources/shared/Stride.NuGetResolver.Targets/Stride.NuGetResolver.Targets.projitems +++ b/sources/shared/Stride.NuGetResolver.Targets/Stride.NuGetResolver.Targets.projitems @@ -22,7 +22,7 @@ $(IntermediateOutputPath)$(MSBuildProjectName).NuGetResolverEntryPoint$(DefaultLanguageSourceExtension) $(TargetFramework) $(TargetFramework)$(TargetPlatformVersion) - STRIDE_NUGET_RESOLVER_UI;$(DefineConstants) + STRIDE_NUGET_RESOLVER_UI;$(DefineConstants) From 4c7dd4ef9976359775d1dfd9c9134df1cd082d01 Mon Sep 17 00:00:00 2001 From: Johan Gustafsson Date: Thu, 12 Jun 2025 14:19:14 +0200 Subject: [PATCH 6/7] restored props, fixed error --- .../Stride.Graphics/Vulkan/VulkanConvertExtensions.cs | 6 +++--- sources/targets/Stride.props | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sources/engine/Stride.Graphics/Vulkan/VulkanConvertExtensions.cs b/sources/engine/Stride.Graphics/Vulkan/VulkanConvertExtensions.cs index 45cc5ade2b..03880d4d9e 100644 --- a/sources/engine/Stride.Graphics/Vulkan/VulkanConvertExtensions.cs +++ b/sources/engine/Stride.Graphics/Vulkan/VulkanConvertExtensions.cs @@ -277,11 +277,11 @@ public static void ConvertPixelFormat(PixelFormat inputFormat, out VkFormat form break; case PixelFormat.R10G10B10A2_UInt: - format = VkFormat.A2R10G10B10UIntPack32; + format = VkFormat.A2R10G10B10UintPack32; pixelSize = 4; break; case PixelFormat.R10G10B10A2_UNorm: - format = VkFormat.A2R10G10B10UNormPack32; + format = VkFormat.A2R10G10B10UnormPack32; pixelSize = 4; break; @@ -406,7 +406,7 @@ public static void ConvertPixelFormat(PixelFormat inputFormat, out VkFormat form pixelSize = 4; break; case PixelFormat.D32_Float_S8X24_UInt: - format = VkFormat.D32SFloatS8UInt; + format = VkFormat.D32SfloatS8Uint; pixelSize = 8; break; diff --git a/sources/targets/Stride.props b/sources/targets/Stride.props index 2e38fae783..734d6cb162 100644 --- a/sources/targets/Stride.props +++ b/sources/targets/Stride.props @@ -15,7 +15,7 @@ - Vulkan + Direct3D11;Direct3D12;OpenGL;OpenGLES;Vulkan From 143c3debbc3f4014ca65b1ae226c8d9ba0c97e8f Mon Sep 17 00:00:00 2001 From: Johan Gustafsson Date: Thu, 12 Jun 2025 17:33:18 +0200 Subject: [PATCH 7/7] Removed unnecessary changes, removed commented out code and added back readonly --- sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs | 6 ------ .../engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs | 2 +- .../Stride.NuGetResolver.Targets.projitems | 2 +- sources/targets/Stride.props | 2 +- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs b/sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs index 0dfb7f7d15..3478021b0f 100644 --- a/sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs +++ b/sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs @@ -1327,12 +1327,6 @@ public unsafe MappedResource MapSubresource(GraphicsResource resource, int subRe throw new InvalidOperationException(); } - // Maybe it just works if removed? - //if (mapMode == MapMode.WriteDiscard) - //{ - // throw new InvalidOperationException("Can't use WriteDiscard on Graphics API that doesn't support renaming"); - //} - if (mapMode != MapMode.WriteNoOverwrite && mapMode != MapMode.Write) { // Need to wait? diff --git a/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs b/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs index f09de808ff..bc56d84718 100644 --- a/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs +++ b/sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs @@ -48,7 +48,7 @@ public partial class GraphicsDevice internal HeapPool DescriptorPools; internal const uint MaxDescriptorSetCount = 256; - internal uint[] MaxDescriptorTypeCounts = + internal readonly uint[] MaxDescriptorTypeCounts = [ 256, // Sampler 0, // CombinedImageSampler diff --git a/sources/shared/Stride.NuGetResolver.Targets/Stride.NuGetResolver.Targets.projitems b/sources/shared/Stride.NuGetResolver.Targets/Stride.NuGetResolver.Targets.projitems index 73c84ac324..6a55e67415 100644 --- a/sources/shared/Stride.NuGetResolver.Targets/Stride.NuGetResolver.Targets.projitems +++ b/sources/shared/Stride.NuGetResolver.Targets/Stride.NuGetResolver.Targets.projitems @@ -22,7 +22,7 @@ $(IntermediateOutputPath)$(MSBuildProjectName).NuGetResolverEntryPoint$(DefaultLanguageSourceExtension) $(TargetFramework) $(TargetFramework)$(TargetPlatformVersion) - STRIDE_NUGET_RESOLVER_UI;$(DefineConstants) + STRIDE_NUGET_RESOLVER_UI;$(DefineConstants) diff --git a/sources/targets/Stride.props b/sources/targets/Stride.props index 734d6cb162..c02a237678 100644 --- a/sources/targets/Stride.props +++ b/sources/targets/Stride.props @@ -15,7 +15,7 @@ - + Direct3D11;Direct3D12;OpenGL;OpenGLES;Vulkan