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 0830f13bd9..3478021b0f 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; @@ -359,14 +373,20 @@ private unsafe void PrepareDraw() 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(); } } - 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 +410,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 +466,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; break; case GraphicsResourceState.GenericRead: texture.NativeLayout = VkImageLayout.General; @@ -503,6 +523,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 +535,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); } /// @@ -1301,11 +1327,6 @@ 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"); - } - 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 97b3bd53f1..bc56d84718 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 readonly uint[] MaxDescriptorTypeCounts = + [ 256, // Sampler 0, // CombinedImageSampler 512, // SampledImage - 0, // StorageImage + 64, // StorageImage 64, // UniformTexelBuffer - 0, // StorageTexelBuffer + 64, // StorageTexelBuffer 512, // UniformBuffer - 0, // StorageBuffer + 64, // 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); @@ -292,11 +308,24 @@ private unsafe void InitializePlatformDevice(GraphicsProfile[] graphicsProfiles, depthClamp = true, }; + vkGetPhysicalDeviceFeatures(NativePhysicalDevice, out var deviceFeatures); + + if (deviceFeatures.shaderStorageImageReadWithoutFormat) + { + enabledFeature.shaderStorageImageReadWithoutFormat = true; + } + + if (deviceFeatures.shaderStorageImageWriteWithoutFormat) + { + enabledFeature.shaderStorageImageWriteWithoutFormat = true; + } + Span supportedExtensionProperties = stackalloc VkUtf8String[] { VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_EXT_DEBUG_MARKER_EXTENSION_NAME, }; + 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/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 5c6b472d8e..03880d4d9e 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 @@ -545,10 +558,17 @@ 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: return VkDescriptorType.UniformTexelBuffer; + case EffectParameterType.StructuredBuffer: + return VkDescriptorType.StorageBuffer; default: throw new NotImplementedException(); @@ -564,9 +584,16 @@ 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: + case EffectParameterType.StructuredBuffer: return VkDescriptorType.StorageBuffer; default: 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..85c4aeaee1 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; @@ -292,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(); @@ -348,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); + } } } } @@ -362,6 +364,13 @@ 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"); + } + + glslShaderWriter.Extensions.Add("GL_EXT_samplerless_texture_functions"); + // 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..7fc637b84a 100644 --- a/sources/shaders/Stride.Core.Shaders/Ast/StorageQualifier.cs +++ b/sources/shaders/Stride.Core.Shaders/Ast/StorageQualifier.cs @@ -21,6 +21,31 @@ public partial class StorageQualifier /// public static readonly Qualifier Uniform = new Qualifier("uniform"); + /// + /// Uniform qualifier. + /// + public static readonly Qualifier Buffer = new Qualifier("buffer"); + + /// + /// 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..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 @@ -142,7 +144,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 +190,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,20 +225,26 @@ 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 +427,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 +796,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 +1072,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; @@ -1074,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) { @@ -1200,6 +1221,48 @@ 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, + "RWTexture2DArray" => 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 +1308,26 @@ 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 == "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 @@ -1260,8 +1341,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 +1350,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 +1398,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; @@ -1329,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); @@ -1340,12 +1421,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); @@ -1368,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); @@ -1465,7 +1546,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 +1573,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 +1668,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 +1756,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 +1785,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 +1859,7 @@ private static string GetStartForStatement(ForStatement forStatement, out int st } } } - + return null; } @@ -1987,7 +2068,7 @@ public override Node Visit(ArrayInitializerExpression arrayCreationExpression) castToType.Arguments.Add(expression); result = castToType; - } + } } return result; @@ -2148,8 +2229,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 +2344,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 +2369,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 +2383,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 +2398,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 +2465,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 +2489,34 @@ 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, + "RWTexture2DArray" => 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)); + } + 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); @@ -2442,7 +2552,7 @@ public override Node Visit(IndexerExpression indexerExpression) return convertRowToColumnMethod; } } - + return indexerExpression; } @@ -2450,9 +2560,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 @@ -2471,10 +2581,6 @@ private void GenerateSamplerMappingAndStrip() AddGlobalDeclaration(textureSampler.Value); } } - else - { - AddGlobalDeclaration(new Variable(StateType.SamplerState, "NoSampler")); - } } /// @@ -2537,6 +2643,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 +2720,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 +2763,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 +2811,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 +3081,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 +3452,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 +3475,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 +3526,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 +3547,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 +3621,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 }); } } @@ -3594,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; @@ -3606,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? @@ -3658,7 +3797,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); } } @@ -3852,7 +3991,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 +4015,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 +4030,7 @@ private TagLayout GetTagLayout(Node node, string alias = null) private void RebindVariableReferenceExpressions() { SearchVisitor.Run( - shader, + shader, node => { if (node is VariableReferenceExpression) @@ -3924,7 +4058,7 @@ private void RebindVariableReferenceExpressions() private void RemoveDefaultParametersForMethods() { SearchVisitor.Run( - shader, + shader, node => { var declaration = node as Parameter; @@ -3991,7 +4125,7 @@ private void RemapCoordinates(StatementList list) private void RenameGlslKeywords() { SearchVisitor.Run( - shader, + shader, node => { var declaration = node as IDeclaration; @@ -4020,7 +4154,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 +4200,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 +4250,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 +4313,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)) @@ -4206,8 +4340,35 @@ 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)) + 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 +4580,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 +4617,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("_"); @@ -4496,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 @@ -4520,7 +4695,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) 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) {