diff --git a/Graphics/GraphicsAccessories/interface/GraphicsAccessories.hpp b/Graphics/GraphicsAccessories/interface/GraphicsAccessories.hpp index 823935fdb..6b4e41f51 100644 --- a/Graphics/GraphicsAccessories/interface/GraphicsAccessories.hpp +++ b/Graphics/GraphicsAccessories/interface/GraphicsAccessories.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2025 Diligent Graphics LLC + * Copyright 2019-2026 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -495,6 +495,8 @@ const char* GetShaderCodeVariableClassString(SHADER_CODE_VARIABLE_CLASS Class); const char* GetShaderCodeBasicTypeString(SHADER_CODE_BASIC_TYPE Type); +Uint32 GetShaderCodeBasicTypeBitSize(SHADER_CODE_BASIC_TYPE Type); + /// Returns the string containing the shader buffer description. String GetShaderCodeBufferDescString(const ShaderCodeBufferDesc& Desc, size_t GlobalIdent = 0, size_t MemberIdent = 2); diff --git a/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp b/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp index 9988f3b9f..755057f5b 100644 --- a/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp +++ b/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2025 Diligent Graphics LLC + * Copyright 2019-2026 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -1720,6 +1720,40 @@ const char* GetShaderCodeBasicTypeString(SHADER_CODE_BASIC_TYPE Type) } } +Uint32 GetShaderCodeBasicTypeBitSize(SHADER_CODE_BASIC_TYPE Type) +{ + static_assert(SHADER_CODE_BASIC_TYPE_COUNT == 21, "Did you add a new type? Please update the switch below."); + switch (Type) + { + // clang-format off + case SHADER_CODE_BASIC_TYPE_UNKNOWN: return 0; + case SHADER_CODE_BASIC_TYPE_VOID: return 0; + case SHADER_CODE_BASIC_TYPE_BOOL: return 8; + case SHADER_CODE_BASIC_TYPE_INT: return 32; + case SHADER_CODE_BASIC_TYPE_INT8: return 8; + case SHADER_CODE_BASIC_TYPE_INT16: return 16; + case SHADER_CODE_BASIC_TYPE_INT64: return 64; + case SHADER_CODE_BASIC_TYPE_UINT: return 32; + case SHADER_CODE_BASIC_TYPE_UINT8: return 8; + case SHADER_CODE_BASIC_TYPE_UINT16: return 16; + case SHADER_CODE_BASIC_TYPE_UINT64: return 64; + case SHADER_CODE_BASIC_TYPE_FLOAT: return 32; + case SHADER_CODE_BASIC_TYPE_FLOAT16: return 16; + case SHADER_CODE_BASIC_TYPE_DOUBLE: return 64; + case SHADER_CODE_BASIC_TYPE_MIN8FLOAT: return 8; + case SHADER_CODE_BASIC_TYPE_MIN10FLOAT: return 10; + case SHADER_CODE_BASIC_TYPE_MIN16FLOAT: return 16; + case SHADER_CODE_BASIC_TYPE_MIN12INT: return 12; + case SHADER_CODE_BASIC_TYPE_MIN16INT: return 16; + case SHADER_CODE_BASIC_TYPE_MIN16UINT: return 16; + case SHADER_CODE_BASIC_TYPE_STRING: return 0; + // clang-format on + default: + UNEXPECTED("Unknown/unsupported variable class"); + return 0; + } +} + static void PrintShaderCodeVariables(std::stringstream& ss, size_t LevelIdent, size_t IdentShift, const ShaderCodeVariableDesc* pVars, Uint32 NumVars) { if (pVars == nullptr || NumVars == 0) diff --git a/Graphics/GraphicsEngineWebGPU/include/PipelineStateWebGPUImpl.hpp b/Graphics/GraphicsEngineWebGPU/include/PipelineStateWebGPUImpl.hpp index cf481bd23..7a18d19cb 100644 --- a/Graphics/GraphicsEngineWebGPU/include/PipelineStateWebGPUImpl.hpp +++ b/Graphics/GraphicsEngineWebGPU/include/PipelineStateWebGPUImpl.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 Diligent Graphics LLC + * Copyright 2023-2026 Diligent Graphics LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -85,6 +85,12 @@ class PipelineStateWebGPUImpl final : public PipelineStateBase SpecConstEntries; + ShaderStageInfo(ShaderWebGPUImpl* _pShader) : Type{_pShader->GetDesc().ShaderType}, pShader{_pShader} diff --git a/Graphics/GraphicsEngineWebGPU/src/EngineFactoryWebGPU.cpp b/Graphics/GraphicsEngineWebGPU/src/EngineFactoryWebGPU.cpp index e048d094d..083e618d8 100644 --- a/Graphics/GraphicsEngineWebGPU/src/EngineFactoryWebGPU.cpp +++ b/Graphics/GraphicsEngineWebGPU/src/EngineFactoryWebGPU.cpp @@ -403,7 +403,7 @@ DeviceFeatures GetSupportedFeatures(WGPUAdapter wgpuAdapter, WGPUDevice wgpuDevi Features.NativeMultiDraw = DEVICE_FEATURE_STATE_DISABLED; Features.AsyncShaderCompilation = DEVICE_FEATURE_STATE_ENABLED; Features.FormattedBuffers = DEVICE_FEATURE_STATE_DISABLED; - Features.SpecializationConstants = DEVICE_FEATURE_STATE_DISABLED; + Features.SpecializationConstants = DEVICE_FEATURE_STATE_ENABLED; Features.TimestampQueries = CheckFeature(WGPUFeatureName_TimestampQuery); Features.DurationQueries = Features.TimestampQueries ? diff --git a/Graphics/GraphicsEngineWebGPU/src/PipelineStateWebGPUImpl.cpp b/Graphics/GraphicsEngineWebGPU/src/PipelineStateWebGPUImpl.cpp index aa2024cae..112209d20 100644 --- a/Graphics/GraphicsEngineWebGPU/src/PipelineStateWebGPUImpl.cpp +++ b/Graphics/GraphicsEngineWebGPU/src/PipelineStateWebGPUImpl.cpp @@ -32,10 +32,125 @@ #include "RenderPassWebGPUImpl.hpp" #include "WebGPUTypeConversions.hpp" #include "WGSLUtils.hpp" +#include "Float16.hpp" namespace Diligent { +namespace +{ + +double ConvertSpecConstantToDouble(const void* pData, Uint32 DataSize, SHADER_CODE_BASIC_TYPE Type) +{ + switch (Type) + { + case SHADER_CODE_BASIC_TYPE_BOOL: + { + // WGSL bool override: any non-zero value is true. + Uint32 Val = 0; + memcpy(&Val, pData, std::min(DataSize, Uint32{4})); + return Val != 0 ? 1.0 : 0.0; + } + + case SHADER_CODE_BASIC_TYPE_INT: + { + Int32 Val = 0; + memcpy(&Val, pData, std::min(DataSize, Uint32{4})); + return static_cast(Val); + } + + case SHADER_CODE_BASIC_TYPE_UINT: + { + Uint32 Val = 0; + memcpy(&Val, pData, std::min(DataSize, Uint32{4})); + return static_cast(Val); + } + + case SHADER_CODE_BASIC_TYPE_FLOAT: + { + float Val = 0; + memcpy(&Val, pData, std::min(DataSize, Uint32{4})); + return static_cast(Val); + } + + case SHADER_CODE_BASIC_TYPE_FLOAT16: + { + Uint16 HalfBits = 0; + memcpy(&HalfBits, pData, std::min(DataSize, Uint32{2})); + return Float16::HalfBitsToFloat(HalfBits); + } + + default: + UNEXPECTED("Unexpected specialization constant type ", GetShaderCodeBasicTypeString(Type)); + return 0; + } +} + +void BuildSpecializationDataWebGPU(PipelineStateWebGPUImpl::TShaderStages& ShaderStages, + Uint32 NumSpecializationConstants, + const SpecializationConstant* pSpecializationConstants, + const PipelineStateDesc& PSODesc) +{ + if (NumSpecializationConstants == 0) + return; + + for (size_t StageIdx = 0; StageIdx < ShaderStages.size(); ++StageIdx) + { + PipelineStateWebGPUImpl::ShaderStageInfo& Stage = ShaderStages[StageIdx]; + const ShaderWebGPUImpl* pShader = Stage.pShader; + const std::shared_ptr& pShaderResources = pShader->GetShaderResources(); + + if (!pShaderResources) + continue; + + for (Uint32 r = 0; r < pShaderResources->GetNumSpecConstants(); ++r) + { + const WGSLSpecializationConstantAttribs& ReflectedSC = pShaderResources->GetSpecConstant(r); + + // Search for a matching user-provided constant by name and stage flag. + const SpecializationConstant* pUserConst = nullptr; + for (Uint32 sc = 0; sc < NumSpecializationConstants; ++sc) + { + const SpecializationConstant& Candidate = pSpecializationConstants[sc]; + if ((Candidate.ShaderStages & Stage.Type) != 0 && + strcmp(Candidate.Name, ReflectedSC.Name) == 0) + { + pUserConst = &Candidate; + break; + } + } + + // No user constant for this reflected entry -- skip silently. + if (pUserConst == nullptr) + continue; + + const SHADER_CODE_BASIC_TYPE ReflectedType = ReflectedSC.GetType(); + const Uint32 ReflectedSize = GetShaderCodeBasicTypeBitSize(ReflectedType) / 8; + + if (pUserConst->Size < ReflectedSize) + { + LOG_ERROR_AND_THROW("Description of ", GetPipelineTypeString(PSODesc.PipelineType), + " PSO '", (PSODesc.Name != nullptr ? PSODesc.Name : ""), + "' is invalid: specialization constant '", pUserConst->Name, + "' in ", GetShaderTypeLiteralName(Stage.Type), + " shader '", pShader->GetDesc().Name, + "' has insufficient data: user provided ", pUserConst->Size, + " bytes, but the shader declares ", + GetShaderCodeBasicTypeString(ReflectedType), + " (", ReflectedSize, " bytes)."); + } + + WGPUConstantEntry Entry{}; + Entry.key = GetWGPUStringView(ReflectedSC.Name); + Entry.value = ConvertSpecConstantToDouble(pUserConst->pData, pUserConst->Size, ReflectedType); + + Stage.SpecConstEntries.push_back(Entry); + } + } +} + +} // anonymous namespace + constexpr INTERFACE_ID PipelineStateWebGPUImpl::IID_InternalImpl; PipelineStateWebGPUImpl::PipelineStateWebGPUImpl(IReferenceCounters* pRefCounters, @@ -342,6 +457,15 @@ struct PipelineStateWebGPUImpl::AsyncPipelineBuilder : public ObjectBaseGetEntryPoint()); + wgpuRenderPipelineDesc.vertex.module = wgpuShaderModules[ShaderIdx].Get(); + wgpuRenderPipelineDesc.vertex.entryPoint = GetWGPUStringView(Stage.pShader->GetEntryPoint()); + wgpuRenderPipelineDesc.vertex.constantCount = Stage.SpecConstEntries.size(); + wgpuRenderPipelineDesc.vertex.constants = Stage.SpecConstEntries.empty() ? nullptr : Stage.SpecConstEntries.data(); break; case SHADER_TYPE_PIXEL: VERIFY(wgpuFragmentState.module == nullptr, "Only one vertex shader is allowed"); wgpuFragmentState.module = wgpuShaderModules[ShaderIdx].Get(); wgpuFragmentState.entryPoint = GetWGPUStringView(Stage.pShader->GetEntryPoint()); + wgpuFragmentState.constantCount = Stage.SpecConstEntries.size(); + wgpuFragmentState.constants = Stage.SpecConstEntries.empty() ? nullptr : Stage.SpecConstEntries.data(); wgpuRenderPipelineDesc.fragment = &wgpuFragmentState; break; @@ -615,6 +750,10 @@ void PipelineStateWebGPUImpl::InitializeWebGPUComputePipeline(const TShaderStage wgpuComputePipelineDesc.compute.entryPoint = GetWGPUStringView(pShaderWebGPU->GetEntryPoint()); wgpuComputePipelineDesc.layout = m_PipelineLayout.GetWebGPUPipelineLayout(); + const std::vector& SpecConstEntries = ShaderStages[0].SpecConstEntries; + wgpuComputePipelineDesc.compute.constantCount = SpecConstEntries.size(); + wgpuComputePipelineDesc.compute.constants = SpecConstEntries.empty() ? nullptr : SpecConstEntries.data(); + if (AsyncBuilder) { // The reference will be released from the callback. diff --git a/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PSOCreationFailureTest.cpp b/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PSOCreationFailureTest.cpp index 117272e93..c3fbca6ff 100644 --- a/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PSOCreationFailureTest.cpp +++ b/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PSOCreationFailureTest.cpp @@ -1760,9 +1760,9 @@ TEST_F(PSOCreationFailureTest, SpecConst_ErrorAtSecondElement) // --------------------------------------------------------------------------- -// Vulkan-specific tests for BuildSpecializationData (Name->SpecId matching). -// These require GLSL shaders with layout(constant_id) declarations so that -// the reflected specialization constants are available for matching. +// Backend-specific tests for specialization constant matching (name-based). +// These require backend-specific shaders with specialization constant +// declarations so that reflected constants are available for matching. // --------------------------------------------------------------------------- // Providing a user constant whose name does not match any reflected constant @@ -1773,8 +1773,8 @@ TEST_F(PSOCreationFailureTest, SpecConst_UnmatchedNameIsSkipped) auto* const pDevice = pEnv->GetDevice(); const auto& DeviceInfo = pDevice->GetDeviceInfo(); - if (!DeviceInfo.IsVulkanDevice()) - GTEST_SKIP() << "Name->SpecId matching is currently Vulkan-only"; + if (!DeviceInfo.IsVulkanDevice() && !DeviceInfo.IsWebGPUDevice()) + GTEST_SKIP() << "Name-based spec constant matching requires Vulkan or WebGPU"; if (DeviceInfo.Features.SpecializationConstants != DEVICE_FEATURE_STATE_ENABLED) GTEST_SKIP() << "Specialization constants are not supported by this device"; @@ -1800,21 +1800,56 @@ TEST_F(PSOCreationFailureTest, SpecConst_UnmatchedNameIsSkipped) } )"; + static constexpr char VSSource_WGSL[] = R"( + override sc_Scale: f32 = 1.0; + + @vertex + fn main() -> @builtin(position) vec4 { + return vec4(0.0, 0.0, 0.0, sc_Scale); + } + )"; + + static constexpr char PSSource_WGSL[] = R"( + @fragment + fn main() -> @location(0) vec4 { + return vec4(0.0, 0.0, 0.0, 1.0); + } + )"; + ShaderCreateInfo ShaderCI; - ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_GLSL_VERBATIM; RefCntAutoPtr pVS; { - ShaderCI.Desc = {"SpecConst UnmatchedName VS", SHADER_TYPE_VERTEX, true}; - ShaderCI.Source = VSSource_GLSL; + ShaderCI.Desc = {"SpecConst UnmatchedName VS", SHADER_TYPE_VERTEX, true}; + ShaderCI.EntryPoint = "main"; + if (DeviceInfo.IsWebGPUDevice()) + { + ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_WGSL; + ShaderCI.Source = VSSource_WGSL; + } + else + { + ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_GLSL_VERBATIM; + ShaderCI.Source = VSSource_GLSL; + } pDevice->CreateShader(ShaderCI, &pVS); ASSERT_TRUE(pVS); } RefCntAutoPtr pPS; { - ShaderCI.Desc = {"SpecConst UnmatchedName PS", SHADER_TYPE_PIXEL, true}; - ShaderCI.Source = PSSource_GLSL; + ShaderCI.Desc = {"SpecConst UnmatchedName PS", SHADER_TYPE_PIXEL, true}; + ShaderCI.EntryPoint = "main"; + if (DeviceInfo.IsWebGPUDevice()) + { + ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_WGSL; + ShaderCI.Source = PSSource_WGSL; + } + else + { + ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_GLSL_VERBATIM; + ShaderCI.Source = PSSource_GLSL; + } pDevice->CreateShader(ShaderCI, &pPS); ASSERT_TRUE(pPS); } @@ -1842,8 +1877,8 @@ TEST_F(PSOCreationFailureTest, SpecConst_InsufficientData) auto* const pDevice = pEnv->GetDevice(); const auto& DeviceInfo = pDevice->GetDeviceInfo(); - if (!DeviceInfo.IsVulkanDevice()) - GTEST_SKIP() << "Name->SpecId matching is currently Vulkan-only"; + if (!DeviceInfo.IsVulkanDevice() && !DeviceInfo.IsWebGPUDevice()) + GTEST_SKIP() << "Name-based spec constant matching requires Vulkan or WebGPU"; if (DeviceInfo.Features.SpecializationConstants != DEVICE_FEATURE_STATE_ENABLED) GTEST_SKIP() << "Specialization constants are not supported by this device"; @@ -1860,10 +1895,30 @@ TEST_F(PSOCreationFailureTest, SpecConst_InsufficientData) } )"; + static constexpr char CSSource_WGSL[] = R"( + override sc_Value: f32 = 1.0; + + @group(0) @binding(0) var g_DummyUAV: texture_storage_2d; + + @compute @workgroup_size(1, 1, 1) + fn main() { + textureStore(g_DummyUAV, vec2(0), vec4(sc_Value)); + } + )"; + ShaderCreateInfo ShaderCI; - ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_GLSL_VERBATIM; - ShaderCI.Desc = {"SpecConst InsufficientData CS", SHADER_TYPE_COMPUTE, true}; - ShaderCI.Source = CSSource_GLSL; + ShaderCI.Desc = {"SpecConst InsufficientData CS", SHADER_TYPE_COMPUTE, true}; + ShaderCI.EntryPoint = "main"; + if (DeviceInfo.IsWebGPUDevice()) + { + ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_WGSL; + ShaderCI.Source = CSSource_WGSL; + } + else + { + ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_GLSL_VERBATIM; + ShaderCI.Source = CSSource_GLSL; + } RefCntAutoPtr pCS; pDevice->CreateShader(ShaderCI, &pCS); diff --git a/Tests/DiligentCoreAPITest/src/SpecializationConstantsTest.cpp b/Tests/DiligentCoreAPITest/src/SpecializationConstantsTest.cpp index 6b30c2e5b..0cb35305b 100644 --- a/Tests/DiligentCoreAPITest/src/SpecializationConstantsTest.cpp +++ b/Tests/DiligentCoreAPITest/src/SpecializationConstantsTest.cpp @@ -83,6 +83,29 @@ static constexpr char g_SpecConstComputeCS_GLSL[] = R"( } )"; +static constexpr char g_SpecConstComputeCS_WGSL[] = R"( + override sc_MulR: f32 = -1.0; + override sc_MulG: f32 = -1.0; + override sc_MulB: f32 = -1.0; + + @group(0) @binding(0) var g_tex2DUAV: texture_storage_2d; + + @compute @workgroup_size(16, 16, 1) + fn main(@builtin(global_invocation_id) gid: vec3) { + let dim = textureDimensions(g_tex2DUAV); + let coord = vec2(gid.xy); + if (coord.x >= i32(dim.x) || coord.y >= i32(dim.y)) { + return; + } + let uv = vec2(gid.xy % 256u) / 256.0; + let Color = vec4(uv.x * sc_MulR, + uv.y * sc_MulG, + uv.x * sc_MulB, + 1.0); + textureStore(g_tex2DUAV, coord, Color); + } +)"; + // Vertex shader: hardcoded positions (same as DrawTest_ProceduralTriangleVS), // per-vertex colors supplied via specialization constants (9 floats). static constexpr char g_SpecConstGraphicsVS_GLSL[] = R"( @@ -128,6 +151,47 @@ static constexpr char g_SpecConstGraphicsVS_GLSL[] = R"( } )"; +static constexpr char g_SpecConstGraphicsVS_WGSL[] = R"( + override sc_Col0_R: f32 = 0.0; + override sc_Col0_G: f32 = 0.0; + override sc_Col0_B: f32 = 0.0; + + override sc_Col1_R: f32 = 0.0; + override sc_Col1_G: f32 = 0.0; + override sc_Col1_B: f32 = 0.0; + + override sc_Col2_R: f32 = 0.0; + override sc_Col2_G: f32 = 0.0; + override sc_Col2_B: f32 = 0.0; + + struct VSOutput { + @builtin(position) Position: vec4, + @location(0) Color: vec3, + }; + + @vertex + fn main(@builtin(vertex_index) VertexIndex: u32) -> VSOutput { + var Pos: array, 6>; + Pos[0] = vec4(-1.0, -0.5, 0.0, 1.0); + Pos[1] = vec4(-0.5, 0.5, 0.0, 1.0); + Pos[2] = vec4( 0.0, -0.5, 0.0, 1.0); + + Pos[3] = vec4( 0.0, -0.5, 0.0, 1.0); + Pos[4] = vec4( 0.5, 0.5, 0.0, 1.0); + Pos[5] = vec4( 1.0, -0.5, 0.0, 1.0); + + var Col: array, 3>; + Col[0] = vec3(sc_Col0_R, sc_Col0_G, sc_Col0_B); + Col[1] = vec3(sc_Col1_R, sc_Col1_G, sc_Col1_B); + Col[2] = vec3(sc_Col2_R, sc_Col2_G, sc_Col2_B); + + var output: VSOutput; + output.Position = Pos[VertexIndex]; + output.Color = Col[VertexIndex % 3]; + return output; + } +)"; + // Fragment shader: interpolated color modulated by specialization constants. // sc_Col0_R is shared with the vertex shader (tests cross-stage matching). // sc_Brightness and sc_AlphaScale are PS-only. @@ -151,6 +215,18 @@ static constexpr char g_SpecConstGraphicsPS_GLSL[] = R"( } )"; +static constexpr char g_SpecConstGraphicsPS_WGSL[] = R"( + override sc_Col0_R: f32 = -1.0; + override sc_Brightness: f32 = -1.0; + override sc_AlphaScale: f32 = -1.0; + + @fragment + fn main(@location(0) in_Color: vec3) -> @location(0) vec4 { + return vec4(vec3(in_Color.r * sc_Col0_R, in_Color.g, in_Color.b) * sc_Brightness, + sc_AlphaScale); + } +)"; + class SpecializationConstants : public ::testing::Test { @@ -189,8 +265,6 @@ TEST_F(SpecializationConstants, ComputePath) ISwapChain* pSwapChain = pEnv->GetSwapChain(); const RenderDeviceInfo& DeviceInfo = pDevice->GetDeviceInfo(); - if (!DeviceInfo.IsVulkanDevice()) - GTEST_SKIP() << "Specialization constants are currently Vulkan-only"; if (DeviceInfo.Features.SpecializationConstants != DEVICE_FEATURE_STATE_ENABLED) GTEST_SKIP() << "Specialization constants are not supported by this device"; if (!DeviceInfo.Features.ComputeShaders) @@ -212,10 +286,19 @@ TEST_F(SpecializationConstants, ComputePath) // --- Spec-const pass: same gradient, channel multipliers via specialization constants --- { ShaderCreateInfo ShaderCI; - ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_GLSL_VERBATIM; - ShaderCI.Desc = {"SpecConst Compute CS", SHADER_TYPE_COMPUTE, true}; - ShaderCI.EntryPoint = "main"; - ShaderCI.Source = g_SpecConstComputeCS_GLSL; + ShaderCI.Desc = {"SpecConst Compute CS", SHADER_TYPE_COMPUTE, true}; + ShaderCI.EntryPoint = "main"; + + if (DeviceInfo.IsWebGPUDevice()) + { + ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_WGSL; + ShaderCI.Source = g_SpecConstComputeCS_WGSL; + } + else + { + ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_GLSL_VERBATIM; + ShaderCI.Source = g_SpecConstComputeCS_GLSL; + } RefCntAutoPtr pCS; pDevice->CreateShader(ShaderCI, &pCS); @@ -278,8 +361,6 @@ TEST_F(SpecializationConstants, GraphicsPath) ISwapChain* pSwapChain = pEnv->GetSwapChain(); const RenderDeviceInfo& DeviceInfo = pDevice->GetDeviceInfo(); - if (!DeviceInfo.IsVulkanDevice()) - GTEST_SKIP() << "Specialization constants are currently Vulkan-only"; if (DeviceInfo.Features.SpecializationConstants != DEVICE_FEATURE_STATE_ENABLED) GTEST_SKIP() << "Specialization constants are not supported by this device"; @@ -304,20 +385,39 @@ TEST_F(SpecializationConstants, GraphicsPath) pContext->ClearRenderTarget(pRTVs[0], ClearColor, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); ShaderCreateInfo ShaderCI; - ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_GLSL_VERBATIM; RefCntAutoPtr pVS; { - ShaderCI.Desc = {"SpecConst Graphics VS", SHADER_TYPE_VERTEX, true}; - ShaderCI.Source = g_SpecConstGraphicsVS_GLSL; + ShaderCI.Desc = {"SpecConst Graphics VS", SHADER_TYPE_VERTEX, true}; + ShaderCI.EntryPoint = "main"; + if (DeviceInfo.IsWebGPUDevice()) + { + ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_WGSL; + ShaderCI.Source = g_SpecConstGraphicsVS_WGSL; + } + else + { + ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_GLSL_VERBATIM; + ShaderCI.Source = g_SpecConstGraphicsVS_GLSL; + } pDevice->CreateShader(ShaderCI, &pVS); ASSERT_NE(pVS, nullptr); } RefCntAutoPtr pPS; { - ShaderCI.Desc = {"SpecConst Graphics PS", SHADER_TYPE_PIXEL, true}; - ShaderCI.Source = g_SpecConstGraphicsPS_GLSL; + ShaderCI.Desc = {"SpecConst Graphics PS", SHADER_TYPE_PIXEL, true}; + ShaderCI.EntryPoint = "main"; + if (DeviceInfo.IsWebGPUDevice()) + { + ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_WGSL; + ShaderCI.Source = g_SpecConstGraphicsPS_WGSL; + } + else + { + ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_GLSL_VERBATIM; + ShaderCI.Source = g_SpecConstGraphicsPS_GLSL; + } pDevice->CreateShader(ShaderCI, &pPS); ASSERT_NE(pPS, nullptr); }