Added glslang support for Vulkan 86/316386/4
authorAdam Bialogonski <adam.b@samsung.com>
Fri, 23 Aug 2024 11:28:30 +0000 (12:28 +0100)
committerAdam Bialogonski <adam.b@samsung.com>
Fri, 23 Aug 2024 13:11:40 +0000 (14:11 +0100)
Shaders can compile to SPIRV in runtime.

Ubuntu glslang dev package is broken so added workaround to deps-check.cmake.

Change-Id: Id6102c743f61ddd324d26dcfa42564ae43977819

14 files changed:
build/tizen/deps-check.cmake [changed mode: 0755->0644]
dali/internal/graphics/file.list
dali/internal/graphics/vulkan-impl/vulkan-graphics-controller.cpp
dali/internal/graphics/vulkan-impl/vulkan-graphics-controller.h
dali/internal/graphics/vulkan-impl/vulkan-program-impl.cpp [new file with mode: 0644]
dali/internal/graphics/vulkan-impl/vulkan-program-impl.h [new file with mode: 0644]
dali/internal/graphics/vulkan-impl/vulkan-program.cpp [new file with mode: 0644]
dali/internal/graphics/vulkan-impl/vulkan-program.h [new file with mode: 0644]
dali/internal/graphics/vulkan-impl/vulkan-shader-impl.cpp [new file with mode: 0644]
dali/internal/graphics/vulkan-impl/vulkan-shader-impl.h [new file with mode: 0644]
dali/internal/graphics/vulkan-impl/vulkan-shader.cpp [new file with mode: 0644]
dali/internal/graphics/vulkan-impl/vulkan-shader.h [new file with mode: 0644]
dali/internal/graphics/vulkan-impl/vulkan-spirv.cpp [new file with mode: 0644]
dali/internal/graphics/vulkan-impl/vulkan-spirv.h [new file with mode: 0644]

old mode 100755 (executable)
new mode 100644 (file)
index fc3bb9f..57b5630
@@ -27,6 +27,13 @@ ARG_ENABLE( ENABLE_RENAME_SO enable_rename_so "${ENABLE_VAL};1" "Specify whether
 ARG_ENABLE( ENABLE_COVERAGE enable_coverage "${ENABLE_VAL}" "Enables coverage" )
 
 ARG_ENABLE( ENABLE_VULKAN enable_vulkan "${ENABLE_VAL}" "Enables Vulkan build")
+
+# force GLSLANG when Vulkan enabled
+IF( enable_vulkan )
+  SET(ENABLE_GLSLANG CACHE STRING "ON")
+  SET(ENABLE_GLSLANG  "ON")
+ENDIF()
+
 ARG_ENABLE( ENABLE_GLSLANG enable_glslang "${ENABLE_VAL}" "Enables Vulkan GLSLang")
 
 # help option
@@ -121,6 +128,7 @@ CHECK_MODULE_AND_SET( LIBUV libuv [] )
 CHECK_MODULE_AND_SET( GLIB glib-2.0 [] )
 CHECK_MODULE_AND_SET( VULKAN vulkan [] )
 CHECK_MODULE_AND_SET( GLSLANG glslang [] )
+CHECK_MODULE_AND_SET( SPIRVTOOLS SPIRV-Tools [] )
 CHECK_MODULE_AND_SET( X11 x11 [] )
 CHECK_MODULE_AND_SET( XCB x11-xcb [] )
 CHECK_MODULE_AND_SET( XDAMAGE xdamage [] )
@@ -529,8 +537,11 @@ IF(enable_glslang)
     FIND_PACKAGE(glslang)
     SET(DALI_LDFLAGS ${DALI_LDFLAGS} glslang::glslang )
   ELSE()
-    SET(DALI_LDFLAGS ${DALI_LDFLAGS} ${GLSLANG_LDFLAGS} )
+    # On Ubuntu 22.04 glslang seems to be horribly broken, pc file doesn't include
+    # all needed deps and SPIRV-Tools package is needed
+    SET(DALI_LDFLAGS ${DALI_LDFLAGS} ${GLSLANG_LDFLAGS} -lSPIRV ${SPIRVTOOLS_LDFLAGS} -lglslang-default-resource-limits)
   ENDIF()
+
 ENDIF()
 
 IF(LIBUV_X11_PROFILE)
index 5aae560a3171908e6d69febafa6103b704bf4be8..17ee6716cd432d1a70f2df6fc0087ded95eed920 100644 (file)
@@ -15,6 +15,7 @@ SET( adaptor_graphics_gles_src_files
 INCLUDE( ${adaptor_graphics_dir}/gles-impl/file.list )
 
 SET( adaptor_graphics_vulkan_src_files
+    ${adaptor_graphics_dir}/common/shader-parser.cpp
     ${adaptor_graphics_dir}/vulkan/vulkan-graphics-impl.cpp
     ${adaptor_graphics_dir}/vulkan/vulkan-graphics-factory.cpp
     ${adaptor_graphics_dir}/vulkan/vulkan-device.cpp
@@ -31,10 +32,15 @@ SET( adaptor_graphics_vulkan_src_files
     ${adaptor_graphics_dir}/vulkan-impl/vulkan-image-view-impl.cpp
     ${adaptor_graphics_dir}/vulkan-impl/vulkan-memory.cpp
     ${adaptor_graphics_dir}/vulkan-impl/vulkan-memory-impl.cpp
+    ${adaptor_graphics_dir}/vulkan-impl/vulkan-program.cpp
+    ${adaptor_graphics_dir}/vulkan-impl/vulkan-program-impl.cpp
     ${adaptor_graphics_dir}/vulkan-impl/vulkan-queue-impl.cpp
     ${adaptor_graphics_dir}/vulkan-impl/vulkan-render-pass.cpp
     ${adaptor_graphics_dir}/vulkan-impl/vulkan-render-pass-impl.cpp
     ${adaptor_graphics_dir}/vulkan-impl/vulkan-render-target.cpp
+    ${adaptor_graphics_dir}/vulkan-impl/vulkan-shader.cpp
+    ${adaptor_graphics_dir}/vulkan-impl/vulkan-shader-impl.cpp
+    ${adaptor_graphics_dir}/vulkan-impl/vulkan-spirv.cpp
     ${adaptor_graphics_dir}/vulkan-impl/vulkan-surface-impl.cpp
     ${adaptor_graphics_dir}/vulkan-impl/vulkan-swapchain-impl.cpp
 )
index 15fe761ddca2c4a747cb9f62e49fd0021fdca787..fc485cadddc8469c2d46730eaa3de3ae67d77742 100644 (file)
 #include <dali/internal/graphics/vulkan-impl/vulkan-fence-impl.h>
 #include <dali/internal/graphics/vulkan-impl/vulkan-framebuffer-impl.h>
 #include <dali/internal/graphics/vulkan-impl/vulkan-memory.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-program.h>
 #include <dali/internal/graphics/vulkan-impl/vulkan-render-pass.h>
 #include <dali/internal/graphics/vulkan-impl/vulkan-render-target.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-shader.h>
 #include <dali/internal/window-system/common/window-render-surface.h>
 
 #if defined(DEBUG_ENABLED)
@@ -297,12 +299,12 @@ UniquePtr<Graphics::Pipeline> VulkanGraphicsController::CreatePipeline(const Gra
 
 UniquePtr<Graphics::Program> VulkanGraphicsController::CreateProgram(const Graphics::ProgramCreateInfo& programCreateInfo, UniquePtr<Graphics::Program>&& oldProgram)
 {
-  return UniquePtr<Graphics::Program>{};
+  return NewObject<Vulkan::Program>(programCreateInfo, *this, std::move(oldProgram));
 }
 
 UniquePtr<Graphics::Shader> VulkanGraphicsController::CreateShader(const Graphics::ShaderCreateInfo& shaderCreateInfo, UniquePtr<Graphics::Shader>&& oldShader)
 {
-  return UniquePtr<Graphics::Shader>{};
+  return NewObject<Vulkan::Shader>(shaderCreateInfo, *this, std::move(oldShader));
 }
 
 UniquePtr<Graphics::Sampler> VulkanGraphicsController::CreateSampler(const Graphics::SamplerCreateInfo& samplerCreateInfo, UniquePtr<Graphics::Sampler>&& oldSampler)
@@ -359,9 +361,9 @@ TextureProperties VulkanGraphicsController::GetTextureProperties(const Graphics:
   return TextureProperties{};
 }
 
-const Reflection& VulkanGraphicsController::GetProgramReflection(const Graphics::Program& program)
+const Graphics::Reflection& VulkanGraphicsController::GetProgramReflection(const Graphics::Program& program)
 {
-  return *(reinterpret_cast<Reflection*>(0));
+  return *(reinterpret_cast<Graphics::Reflection*>(0));
 }
 
 bool VulkanGraphicsController::PipelineEquals(const Graphics::Pipeline& pipeline0, const Graphics::Pipeline& pipeline1) const
@@ -454,6 +456,11 @@ void VulkanGraphicsController::DiscardResource(Vulkan::Buffer* buffer)
   // @todo Add discard queues
 }
 
+void VulkanGraphicsController::DiscardResource(Vulkan::Program* program)
+{
+  // @todo Add discard queues
+}
+
 Vulkan::Device& VulkanGraphicsController::GetGraphicsDevice()
 {
   return *mImpl->mGraphicsDevice;
index 311da77f39b4309aed31070813a6a2b06885554a..156bb59ae2ab063d3a7cdfd7c427cae4482f0c2f 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <dali/integration-api/debug.h>
 #include <dali/internal/graphics/vulkan-impl/vulkan-framebuffer.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-program.h>
 #include <dali/internal/graphics/vulkan-impl/vulkan-render-target.h>
 
 namespace Dali
@@ -356,6 +357,7 @@ public:
   void Add(Vulkan::RenderTarget* renderTarget);
   void DiscardResource(Vulkan::RenderTarget* renderTarget);
   void DiscardResource(Vulkan::Buffer* buffer);
+  void DiscardResource(Vulkan::Program* renderProgram);
 
 public: // Integration::GraphicsConfig
   bool        IsBlendEquationSupported(DevelBlendEquation::Type blendEquation) override;
diff --git a/dali/internal/graphics/vulkan-impl/vulkan-program-impl.cpp b/dali/internal/graphics/vulkan-impl/vulkan-program-impl.cpp
new file mode 100644 (file)
index 0000000..87f109f
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include <dali/internal/graphics/vulkan-impl/vulkan-program-impl.h>
+
+// INTERNAL HEADERS
+#include <dali/integration-api/debug.h>
+#include <dali/internal/graphics/common/shader-parser.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-graphics-controller.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-shader-impl.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-shader.h>
+
+// EXTERNAL HEADERS
+#include <iostream>
+
+#if defined(DEBUG_ENABLED)
+extern Debug::Filter* gGraphicsProgramLogFilter;
+#endif
+
+namespace Dali::Graphics::Vulkan
+{
+struct ProgramImpl::Impl
+{
+  explicit Impl(VulkanGraphicsController& _controller, const ProgramCreateInfo& info)
+  : controller(_controller)
+  {
+    createInfo = info;
+    if(info.shaderState)
+    {
+      createInfo.shaderState = new std::vector<ShaderState>(*info.shaderState);
+    }
+
+    // Create new reference of std::string_view.
+    name            = std::string(info.name);
+    createInfo.name = name;
+  }
+
+  ~Impl()
+  {
+    delete createInfo.shaderState;
+  }
+
+  VulkanGraphicsController& controller;
+  ProgramCreateInfo         createInfo;
+  std::string               name;
+  uint32_t                  refCount{0u};
+};
+
+ProgramImpl::ProgramImpl(const Graphics::ProgramCreateInfo& createInfo, VulkanGraphicsController& controller)
+{
+  // Create implementation
+  mImpl = std::make_unique<Impl>(controller, createInfo);
+
+  // Preprocess source code, if successful, compile shaders
+  bool success = true;
+  if(Preprocess())
+  {
+    for(const auto& state : *createInfo.shaderState)
+    {
+      auto shader     = static_cast<const Vulkan::Shader*>(state.shader);
+      auto shaderImpl = shader->GetImplementation();
+      if(!shaderImpl->Compile())
+      {
+        DALI_LOG_ERROR("SPIRV Compilation failed!\n");
+      }
+    }
+  }
+}
+
+ProgramImpl::~ProgramImpl() = default;
+
+bool ProgramImpl::Destroy()
+{
+  return false;
+}
+
+bool ProgramImpl::Preprocess()
+{
+  // For now only Vertex and Fragment shader stages supported
+  // and one per stage
+  std::string  vertexString;
+  std::string  fragmentString;
+  std::string* currentString = nullptr;
+
+  const Vulkan::Shader* vsh = nullptr;
+  const Vulkan::Shader* fsh = nullptr;
+
+  const auto& info = mImpl->createInfo;
+
+  for(const auto& state : *info.shaderState)
+  {
+    const auto* shader = static_cast<const Vulkan::Shader*>(state.shader);
+    if(state.pipelineStage == PipelineStage::VERTEX_SHADER)
+    {
+      // Only TEXT source mode can be processed
+      currentString = &vertexString;
+      vsh           = shader;
+    }
+    else if(state.pipelineStage == PipelineStage::FRAGMENT_SHADER)
+    {
+      // Only TEXT source mode can be processed
+      currentString = &fragmentString;
+      fsh           = shader;
+    }
+    else
+    {
+      // no valid stream to push
+      currentString = nullptr;
+      DALI_LOG_ERROR("Shader state contains invalid shader source (most likely binary)! Can't process!");
+    }
+
+    // Check if stream valid
+    if(currentString && currentString->empty() && shader->GetCreateInfo().sourceMode == ShaderSourceMode::TEXT)
+    {
+      *currentString = std::string(reinterpret_cast<const char*>(shader->GetCreateInfo().sourceData),
+                                   shader->GetCreateInfo().sourceSize);
+    }
+    else
+    {
+      DALI_LOG_ERROR("Preprocessing of binary shaders isn't allowed!");
+    }
+  }
+
+  // if we have both streams ready
+  if(!vertexString.empty() && !fragmentString.empty())
+  {
+    // In case we have one modern shader and one legacy counterpart we need to enforce
+    // output language.
+    Internal::ShaderParser::ShaderParserInfo parseInfo{};
+    parseInfo.vertexShaderCode            = &vertexString;
+    parseInfo.fragmentShaderCode          = &fragmentString;
+    parseInfo.vertexShaderLegacyVersion   = vsh->GetGLSLVersion();
+    parseInfo.fragmentShaderLegacyVersion = fsh->GetGLSLVersion();
+    parseInfo.language                    = Internal::ShaderParser::OutputLanguage::SPIRV_GLSL;
+    parseInfo.outputVersion               = std::max(vsh->GetGLSLVersion(), fsh->GetGLSLVersion());
+
+    std::vector<std::string> newShaders;
+
+    Internal::ShaderParser::Parse(parseInfo, newShaders);
+
+    // substitute shader code
+    vsh->GetImplementation()->SetPreprocessedCode(newShaders[0].data(), newShaders[0].size());
+    fsh->GetImplementation()->SetPreprocessedCode(newShaders[1].data(), newShaders[1].size());
+    return true;
+  }
+  else
+  {
+    DALI_LOG_ERROR("Preprocessing shader code failed!");
+    return false;
+  }
+}
+
+bool ProgramImpl::Create()
+{
+  // TODO: redirect to the reflection builder
+  return true;
+}
+
+uint32_t ProgramImpl::GetSPIRVProgram() const
+{
+  return {};
+}
+
+uint32_t ProgramImpl::Retain()
+{
+  return ++mImpl->refCount;
+}
+
+uint32_t ProgramImpl::Release()
+{
+  return --mImpl->refCount;
+}
+
+uint32_t ProgramImpl::GetRefCount() const
+{
+  return mImpl->refCount;
+}
+
+bool ProgramImpl::GetParameter(uint32_t parameterId, void* out)
+{
+  return false;
+}
+
+VulkanGraphicsController& ProgramImpl::GetController() const
+{
+  return mImpl->controller;
+}
+
+const ProgramCreateInfo& ProgramImpl::GetCreateInfo() const
+{
+  return mImpl->createInfo;
+}
+
+}; // namespace Dali::Graphics::Vulkan
diff --git a/dali/internal/graphics/vulkan-impl/vulkan-program-impl.h b/dali/internal/graphics/vulkan-impl/vulkan-program-impl.h
new file mode 100644 (file)
index 0000000..a651f73
--- /dev/null
@@ -0,0 +1,137 @@
+#ifndef DALI_GRAPHICS_VULKAN_PROGRAM_IMPL_H
+#define DALI_GRAPHICS_VULKAN_PROGRAM_IMPL_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/graphics-api/graphics-program-create-info.h>
+#include <dali/graphics-api/graphics-program.h>
+
+// INTERNAL INCLUDES
+#include <dali/internal/graphics/vulkan-impl/vulkan-graphics-resource.h>
+
+namespace Dali::Graphics::Vulkan
+{
+/**
+ * @brief Program implementation
+ *
+ * Program implementation is owned only by the PipelineCache
+ *
+ * Like pipeline, it's created and managed by the PipelineCache
+ */
+class ProgramImpl
+{
+public:
+  /**
+   * @brief Constructor
+   *
+   * @param[in] createInfo Valid create info structure
+   * @param[in] controller Valid reference to the controller object
+   */
+  ProgramImpl(const Graphics::ProgramCreateInfo& createInfo, VulkanGraphicsController& controller);
+
+  /**
+   * @brief Destructor
+   */
+  ~ProgramImpl();
+
+  /**
+   * @brief Destroys Vulkan resources associated with the Program
+   *
+   * @return True on success
+   */
+  bool Destroy();
+
+  /**
+   * @brief Creates Vulkan resource for this Program
+   *
+   * @return True on success
+   */
+  bool Create();
+
+  /**
+   * @brief Preprocesses shaders
+   */
+  bool Preprocess();
+
+  /**
+   * @brief Returns Graphics program id
+   *
+   * Since Vulkan has no concept of Program, the Graphics resource
+   * should be returned.
+   *
+   * @return Graphics program id
+   */
+  [[nodiscard]] uint32_t GetSPIRVProgram() const;
+
+  /**
+   * @brief Increases ref count
+   *
+   * @return new refcount
+   */
+  uint32_t Retain();
+
+  /**
+   * @brief Decreases ref count
+   *
+   * @return New refcount
+   */
+  uint32_t Release();
+
+  /**
+   * @brief Retrieves ref count
+   * @return Refcount value
+   */
+  [[nodiscard]] uint32_t GetRefCount() const;
+
+  /**
+   * @brief Returns controller
+   *
+   * @return Valid Controller object
+   */
+  [[nodiscard]] VulkanGraphicsController& GetController() const;
+
+  /**
+   * @brief Returns create info structure
+   *
+   * @return Reference to valid create info structure
+   */
+  [[nodiscard]] const ProgramCreateInfo& GetCreateInfo() const;
+
+  /**
+   * @brief Returns parameter value specified by parameterId
+   *
+   * This function can be used as a backdoor into the implementation
+   * used to retrieve internal data.
+   *
+   * @param[in] parameterId Integer parameter id
+   * @param[out] out Pointer to write to
+   *
+   * @return True on success
+   */
+  bool GetParameter(uint32_t parameterId, void* out);
+
+private:
+  friend class Program;
+
+  struct Impl;
+  std::unique_ptr<Impl> mImpl; // TODO: see if we need it (PipelineCache related)
+};
+
+} // namespace Dali::Graphics::Vulkan
+
+#endif //DALI_GRAPHICS_VULKAN_PROGRAM_IMPL_H
diff --git a/dali/internal/graphics/vulkan-impl/vulkan-program.cpp b/dali/internal/graphics/vulkan-impl/vulkan-program.cpp
new file mode 100644 (file)
index 0000000..7a6e1b4
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include <dali/internal/graphics/vulkan-impl/vulkan-program.h>
+
+// INTERNAL HEADERS
+#include <dali/integration-api/debug.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-graphics-controller.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-program-impl.h>
+
+// EXTERNAL HEADERS
+#include <iostream>
+
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gGraphicsProgramLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_GRAPHICS_PROGRAM");
+#endif
+
+namespace Dali::Graphics::Vulkan
+{
+Program::Program(const Graphics::ProgramCreateInfo& createInfo, VulkanGraphicsController& controller)
+{
+  mProgram = new ProgramImpl(createInfo, controller);
+}
+
+Program::~Program()
+{
+  // Destroy Vulkan resources of implementation. This should happen
+  // only if there's no more pipelines using this program so
+  // it is safe to do it in the destructor
+  // TODO: implement it via cache!
+  if(!mProgram->Release())
+  {
+    mProgram->Destroy();
+    delete mProgram;
+  }
+}
+
+const Vulkan::Reflection& Program::GetReflection() const
+{
+  // TODO: Implement reflection
+  return *reinterpret_cast<Vulkan::Reflection*>(0u);
+}
+
+VulkanGraphicsController& Program::GetController() const
+{
+  return GetImplementation()->GetController();
+}
+
+const ProgramCreateInfo& Program::GetCreateInfo() const
+{
+  return GetImplementation()->GetCreateInfo();
+}
+
+void Program::DiscardResource()
+{
+  GetController().DiscardResource(this);
+}
+
+}; // namespace Dali::Graphics::Vulkan
diff --git a/dali/internal/graphics/vulkan-impl/vulkan-program.h b/dali/internal/graphics/vulkan-impl/vulkan-program.h
new file mode 100644 (file)
index 0000000..041bbe0
--- /dev/null
@@ -0,0 +1,132 @@
+#ifndef DALI_GRAPHICS_VULKAN_PROGRAM_H
+#define DALI_GRAPHICS_VULKAN_PROGRAM_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include <dali/graphics-api/graphics-program.h>
+
+// INTERNAL INCLUDES
+#include <dali/graphics-api/graphics-program-create-info.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-graphics-resource.h>
+
+namespace Dali::Graphics::Vulkan
+{
+class Reflection;
+class ProgramImpl;
+/**
+ * @brief Wrapper for the program implementation
+ *
+ * This object is returned back to the client-side
+ *
+ * Vulkan has no concept of Program. The Vulkan program
+ * within Graphics API is a set of shader stages linked together
+ * so the reflection can do its work on it.
+ */
+class Program : public Graphics::Program
+{
+public:
+  /**
+   * @brief Constructor
+   *
+   * @param[in] impl Pointer to valid implementation
+   */
+  explicit Program(ProgramImpl* impl)
+  {
+    // TODO: needs PipelineCache
+  }
+
+  Program(const Graphics::ProgramCreateInfo& createInfo, VulkanGraphicsController& controller);
+
+  /**
+   * @brief Destructor
+   */
+  ~Program() override;
+
+  /**
+   * @brief Returns reference to the Reflection object
+
+   * @return Reflection
+   */
+  [[nodiscard]] const Vulkan::Reflection& GetReflection() const;
+
+  /**
+   * @brief Retrieves internal program implementation
+   *
+   * @return Valid pointer to the ProgramImpl object
+   */
+  [[nodiscard]] ProgramImpl* GetImplementation() const
+  {
+    return mProgram;
+  }
+
+  /**
+   * @brief Returns controller
+   *
+   * @return controller
+   */
+  [[nodiscard]] VulkanGraphicsController& GetController() const;
+
+  /**
+   * @brief Returns create info structure
+   *
+   * @return create info structure
+   */
+  [[nodiscard]] const ProgramCreateInfo& GetCreateInfo() const;
+
+  bool operator==(const Vulkan::Program& program) const
+  {
+    return (program.mProgram == mProgram);
+  }
+
+  bool operator==(const Vulkan::ProgramImpl* programImpl) const
+  {
+    return (programImpl == mProgram);
+  }
+
+  bool operator!=(const Vulkan::Program& program) const
+  {
+    return (program.mProgram != mProgram);
+  }
+
+  /**
+   * @brief Run by UniquePtr to discard resource
+   */
+  void DiscardResource();
+
+  /**
+   * @brief Destroying resources
+   *
+   * This function is kept for compatibility with Resource<> class
+   * so can the object can be use with templated functions.
+   */
+  void DestroyResource()
+  {
+    // nothing to do here
+  }
+
+  bool TryRecycle(const Graphics::ProgramCreateInfo& createInfo, VulkanGraphicsController& controller)
+  {
+    return false;
+  }
+
+private:
+  ProgramImpl* mProgram{nullptr};
+};
+} // namespace Dali::Graphics::Vulkan
+
+#endif //DALI_GRAPHICS_VULKAN_PROGRAM_H
diff --git a/dali/internal/graphics/vulkan-impl/vulkan-shader-impl.cpp b/dali/internal/graphics/vulkan-impl/vulkan-shader-impl.cpp
new file mode 100644 (file)
index 0000000..3ecc55a
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali/internal/graphics/vulkan-impl/vulkan-shader-impl.h>
+
+// INTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-graphics-controller.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-spirv.h>
+#include <dali/internal/graphics/vulkan/vulkan-device.h>
+
+namespace Dali::Graphics::Vulkan
+{
+struct ShaderImpl::Impl
+{
+  explicit Impl(Graphics::Vulkan::VulkanGraphicsController& _controller, const Graphics::ShaderCreateInfo& _createInfo)
+  : controller(_controller)
+  {
+    createInfo.pipelineStage  = _createInfo.pipelineStage;
+    createInfo.shaderlanguage = _createInfo.shaderlanguage;
+    createInfo.sourceMode     = _createInfo.sourceMode;
+    createInfo.shaderVersion  = _createInfo.shaderVersion;
+
+    if(createInfo.sourceMode == ShaderSourceMode::TEXT)
+    {
+      if(createInfo.shaderVersion > 0)
+      {
+        // Make a copy of source code. if code is meant to be used
+        // by modern parser, skip the prefix part
+        size_t dataStartIndex = 0;
+        size_t dataSize;
+
+        ShaderImpl::StripLegacyCodeIfNeeded(_createInfo, dataStartIndex, glslVersion, dataSize);
+
+        source.resize(dataSize);
+        std::copy(reinterpret_cast<const uint8_t*>(_createInfo.sourceData) + dataStartIndex,
+                  reinterpret_cast<const uint8_t*>(_createInfo.sourceData) + dataStartIndex + dataSize,
+                  source.data());
+
+        // Substitute pointer
+        createInfo.sourceData = source.data();
+        createInfo.sourceSize = dataSize;
+      }
+    }
+    else // if binary format, we expect ready to use SPIRV shader module
+    {
+      // Make a copy of spirv data
+      source.resize(_createInfo.sourceSize);
+      std::copy(reinterpret_cast<const uint8_t*>(_createInfo.sourceData),
+                reinterpret_cast<const uint8_t*>(_createInfo.sourceData) + _createInfo.sourceSize,
+                source.data());
+      createInfo.sourceData = source.data();
+      createInfo.sourceSize = _createInfo.sourceSize;
+    }
+  }
+
+  ~Impl() = default;
+
+  bool Compile()
+  {
+    bool success = true;
+    if(createInfo.sourceMode == ShaderSourceMode::TEXT)
+    {
+      SPIRVGeneratorInfo info;
+      info.pipelineStage = createInfo.pipelineStage;
+      auto shaderCode    = std::string_view(reinterpret_cast<char*>(sourcePreprocessed.data()));
+      info.shaderCode    = shaderCode;
+
+      spirv = std::make_unique<SPIRVGenerator>(info);
+
+      spirv->Generate();
+      if(spirv->IsValid())
+      {
+        // substitute data and size with compiled code
+        createInfo.sourceSize = spirv->Get().size() * 4u;
+        createInfo.sourceData = spirv->Get().data();
+      }
+      else
+      {
+        success = false;
+      }
+    }
+    if(!success)
+    {
+      return false;
+    }
+
+    // Create Vulkan shader module
+    auto&                      gfxDevice = controller.GetGraphicsDevice();
+    auto                       vkDevice  = gfxDevice.GetLogicalDevice();
+    vk::ShaderModuleCreateInfo info;
+    info.pCode    = reinterpret_cast<const uint32_t*>(createInfo.sourceData);
+    info.codeSize = size_t(createInfo.sourceSize);
+    VkAssert(vkDevice.createShaderModule(&info, &gfxDevice.GetAllocator(), &shaderModule));
+
+    return true;
+  }
+
+  void Destroy()
+  {
+    auto vkDevice = controller.GetGraphicsDevice().GetLogicalDevice();
+    vkDevice.destroyShaderModule(shaderModule, controller.GetGraphicsDevice().GetAllocator());
+  }
+
+  void SetPreprocessedCode(void* data, uint32_t size)
+  {
+    sourcePreprocessed.resize(size);
+
+    std::copy(reinterpret_cast<const uint8_t*>(data),
+              reinterpret_cast<const uint8_t*>(data) + size,
+              sourcePreprocessed.data());
+  }
+
+  VulkanGraphicsController&       controller;
+  ShaderCreateInfo                createInfo;
+  std::vector<uint8_t>            source{};
+  std::vector<uint8_t>            sourcePreprocessed{};
+  std::unique_ptr<SPIRVGenerator> spirv;
+  vk::ShaderModule                shaderModule;
+
+  uint32_t refCount{0u};
+  uint32_t flushCount{0u};  ///< Number of frames at refCount=0
+  uint32_t glslVersion{0u}; ///< 0 - unknown, otherwise valid #version like 130, 300, etc.
+};
+
+ShaderImpl::ShaderImpl(const Graphics::ShaderCreateInfo& createInfo, Graphics::Vulkan::VulkanGraphicsController& controller)
+{
+  mImpl = std::make_unique<Impl>(controller, createInfo);
+}
+
+ShaderImpl::~ShaderImpl() = default;
+
+uint32_t ShaderImpl::Retain()
+{
+  mImpl->flushCount = 0;
+  return ++mImpl->refCount;
+}
+
+uint32_t ShaderImpl::Release()
+{
+  uint32_t remainingCount = --mImpl->refCount;
+  mImpl->flushCount       = 0;
+  return remainingCount;
+}
+
+[[nodiscard]] uint32_t ShaderImpl::GetRefCount() const
+{
+  return mImpl->refCount;
+}
+
+[[nodiscard]] uint32_t ShaderImpl::IncreaseFlushCount()
+{
+  return ++mImpl->flushCount;
+}
+
+[[nodiscard]] uint32_t ShaderImpl::GetFlushCount() const
+{
+  return mImpl->flushCount;
+}
+
+[[nodiscard]] uint32_t ShaderImpl::GetGLSLVersion() const
+{
+  return mImpl->glslVersion;
+}
+
+/**
+ * @brief Compiles shader
+ *
+ * @return True on success
+ */
+[[nodiscard]] bool ShaderImpl::Compile() const
+{
+  return mImpl->Compile();
+}
+
+[[nodiscard]] vk::ShaderModule ShaderImpl::GetVkShaderModule() const
+{
+  return mImpl->shaderModule;
+}
+
+const ShaderCreateInfo& ShaderImpl::GetCreateInfo() const
+{
+  return mImpl->createInfo;
+}
+
+[[nodiscard]] VulkanGraphicsController& ShaderImpl::GetController() const
+{
+  return mImpl->controller;
+}
+
+void ShaderImpl::StripLegacyCodeIfNeeded(const ShaderCreateInfo& info, size_t& startIndex, uint32_t& glslVersion, size_t& finalDataSize)
+{
+  // Make a copy of source code. if code is meant to be used
+  // by modern parser, skip the prefix part
+  auto text   = reinterpret_cast<const char*>(info.sourceData);
+  auto result = std::string_view(text).find("//@legacy-prefix-end");
+  glslVersion = 0u;
+  if(info.shaderVersion != 0)
+  {
+    if(result != 0 && result != std::string::npos)
+    {
+      DALI_LOG_ERROR("Shader processing: @legacy-prefix-end must be a very first statement!\n");
+    }
+    else if(result == 0)
+    {
+      char* end;
+      startIndex = std::strtoul(reinterpret_cast<const char*>(info.sourceData) + 21, &end, 10);
+    }
+  }
+  else
+  {
+    // For legacy shaders we need to make sure that the #version is a very first line
+    // so need to strip //@legacy-prefix-end tag
+    auto versionPos = std::string_view(text).find("#version", 0);
+    if(versionPos == std::string::npos)
+    {
+      startIndex = 0; // not trimming anything
+
+      // if there's no version yet it's a legacy shader we assign 100
+      glslVersion = 100;
+    }
+    else
+    {
+      // save version of legacy shader
+      char* end;
+      glslVersion = uint32_t(std::strtol(std::string_view(text).data() + versionPos + 9, &end, 10));
+      startIndex  = versionPos;
+    }
+  }
+  finalDataSize = info.sourceSize - startIndex;
+}
+
+void ShaderImpl::SetPreprocessedCode(void* data, uint32_t size)
+{
+  mImpl->SetPreprocessedCode(data, size);
+}
+
+} // namespace Dali::Graphics::Vulkan
diff --git a/dali/internal/graphics/vulkan-impl/vulkan-shader-impl.h b/dali/internal/graphics/vulkan-impl/vulkan-shader-impl.h
new file mode 100644 (file)
index 0000000..288dac4
--- /dev/null
@@ -0,0 +1,138 @@
+#ifndef DALI_GRAPHICS_VULKAN_SHADER_IMPL_H
+#define DALI_GRAPHICS_VULKAN_SHADER_IMPL_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali/graphics-api/graphics-shader-create-info.h>
+#include <dali/graphics-api/graphics-shader.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-graphics-resource.h>
+
+// EXTERNAL INCLUDES
+#include <vulkan/vulkan.hpp>
+
+namespace Dali::Graphics::Vulkan
+{
+class ShaderImpl
+{
+public:
+  /**
+   * @brief Constructor
+   * @param[in] createInfo Valid createInfo structure
+   * @param[in] controller Reference to the controller
+   */
+  ShaderImpl(const Graphics::ShaderCreateInfo& createInfo, Graphics::Vulkan::VulkanGraphicsController& controller);
+
+  /**
+   * @brief destructor
+   */
+  ~ShaderImpl();
+
+  /**
+   * @brief Increases ref count
+   * @return ref count after increment
+   */
+  uint32_t Retain();
+
+  /**
+   * @brief Decreases refcount
+   * @return ref count after decrement
+   */
+  uint32_t Release();
+
+  /**
+   * @brief returns current ref count
+   * @return current ref count
+   */
+  [[nodiscard]] uint32_t GetRefCount() const;
+
+  /**
+   * Whilst unreferenced, increase the flush count and return it
+   *
+   * @return The new flush count
+   */
+  [[nodiscard]] uint32_t IncreaseFlushCount();
+
+  /**
+   * Get the flush count whilst unreferenced
+   *
+   * @return the flush count
+   */
+  [[nodiscard]] uint32_t GetFlushCount() const;
+
+  /**
+   * @brief Compiles shader
+   *
+   * @return True on success
+   */
+  [[nodiscard]] bool Compile() const;
+
+  /**
+   * @brief Destroys Vulkan shader module
+   */
+  void Destroy();
+
+  /**
+   * @brief Returns Vulkan resource
+   * @return Valid Vulkan shader resource
+   */
+  vk::ShaderModule GetVkShaderModule() const;
+
+  /**
+   * @brief Returns create info structure
+   * @return Returns valid create info structure
+   */
+  [[nodiscard]] const ShaderCreateInfo& GetCreateInfo() const;
+
+  /**
+   * @brief Returns reference to the graphics controller
+   * @return Valid reference to the graphics controller
+   */
+  [[nodiscard]] VulkanGraphicsController& GetController() const;
+
+  /**
+   * Strips legacy prefix from the GLSL source code if necessary
+   * @param info valid ShaderCreateInfo structure
+   * @param[out] startIndex Start index of the source code
+   * @param[out] glslVersion Detected GLSL version of legacy shader
+   * @param[out] finalDataSize Size of trimmed data
+   */
+  static void StripLegacyCodeIfNeeded(const ShaderCreateInfo& info, size_t& startIndex, uint32_t& glslVersion, size_t& finalDataSize);
+
+  /**
+   * @brief Sets preprocess code
+   * @param[in] data Valid pointer to the new source code
+   * @param[in] size Size of the source code
+   */
+  void SetPreprocessedCode(void* data, uint32_t size);
+
+  /**
+   * @brief Returns GLSL version
+   * @return Returns valid GLSL version or 0 if undefined
+   */
+  [[nodiscard]] uint32_t GetGLSLVersion() const;
+
+private:
+  friend class Shader;
+  struct Impl;
+  std::unique_ptr<Impl> mImpl{nullptr}; // TODO: see if we need it (PipelineCache related)
+};
+
+} // namespace Dali::Graphics::Vulkan
+
+#endif
diff --git a/dali/internal/graphics/vulkan-impl/vulkan-shader.cpp b/dali/internal/graphics/vulkan-impl/vulkan-shader.cpp
new file mode 100644 (file)
index 0000000..ad602e6
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include "vulkan-shader.h"
+
+// INTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+#include "vulkan-graphics-controller.h"
+#include "vulkan-shader-impl.h"
+
+namespace Dali::Graphics::Vulkan
+{
+Shader::Shader(ShaderImpl* impl)
+{
+  mShader = impl;
+  mShader->Retain(); // TODO: we may not need it at all
+}
+
+Shader::Shader(const Graphics::ShaderCreateInfo& createInfo, VulkanGraphicsController& controller)
+{
+  mShader = new ShaderImpl(createInfo, controller);
+}
+
+Shader::~Shader()
+{
+  if(!mShader->Release())
+  {
+    // TODO: handle pipeline cache
+    //GetImplementation()->GetController().GetPipelineCache().MarkShaderCacheFlushRequired();
+  }
+
+  // No cache, delete implementation
+  delete mShader;
+}
+
+bool Shader::TryRecycle(const Graphics::ShaderCreateInfo& createInfo, VulkanGraphicsController& controller)
+{
+  return false;
+}
+
+[[nodiscard]] const ShaderCreateInfo& Shader::GetCreateInfo() const
+{
+  return GetImplementation()->GetCreateInfo();
+}
+
+void Shader::DiscardResource()
+{
+}
+
+uint32_t Shader::GetGLSLVersion() const
+{
+  return GetImplementation()->GetGLSLVersion();
+}
+
+} // namespace Dali::Graphics::Vulkan
diff --git a/dali/internal/graphics/vulkan-impl/vulkan-shader.h b/dali/internal/graphics/vulkan-impl/vulkan-shader.h
new file mode 100644 (file)
index 0000000..0a932c2
--- /dev/null
@@ -0,0 +1,93 @@
+#ifndef DALI_GRAPHICS_VULKAN_SHADER_H
+#define DALI_GRAPHICS_VULKAN_SHADER_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali/graphics-api/graphics-shader-create-info.h>
+#include <dali/graphics-api/graphics-shader.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-graphics-resource.h>
+
+namespace Dali::Graphics::Vulkan
+{
+class ShaderImpl;
+class Shader : public Graphics::Shader
+{
+public:
+  /**
+   * @brief Constructor
+   *
+   * @param[in] impl Pointer to valid implementation
+   */
+  explicit Shader(ShaderImpl* impl); // TODO: this should be controlled by cache
+
+  Shader(const Graphics::ShaderCreateInfo& createInfo, VulkanGraphicsController& controller);
+
+  /**
+   * @brief Destructor
+   */
+  ~Shader() override;
+
+  [[nodiscard]] ShaderImpl* GetImplementation() const
+  {
+    return mShader;
+  }
+
+  [[nodiscard]] const ShaderCreateInfo& GetCreateInfo() const;
+
+  bool operator==(const Vulkan::Shader& shader) const
+  {
+    return (shader.mShader == mShader);
+  }
+
+  bool operator==(const Vulkan::ShaderImpl* shaderImpl) const
+  {
+    return (shaderImpl == mShader);
+  }
+
+  bool operator!=(const Vulkan::Shader& shader) const
+  {
+    return (shader.mShader != mShader);
+  }
+
+  /**
+   * @brief Called when UniquePtr<> on client-side dies.
+   */
+  void DiscardResource();
+
+  /**
+   * @brief Destroying Vulkan resources
+   *
+   * This function is kept for compatibility with Resource<> class
+   * so can the object can be use with templated functions.
+   */
+  void DestroyResource()
+  {
+    // nothing to do here
+  }
+  bool TryRecycle(const Graphics::ShaderCreateInfo& createInfo, VulkanGraphicsController& controller);
+
+  [[nodiscard]] uint32_t GetGLSLVersion() const;
+
+private:
+  ShaderImpl* mShader{nullptr};
+};
+
+} // namespace Dali::Graphics::Vulkan
+
+#endif
diff --git a/dali/internal/graphics/vulkan-impl/vulkan-spirv.cpp b/dali/internal/graphics/vulkan-impl/vulkan-spirv.cpp
new file mode 100644 (file)
index 0000000..fb6fae6
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+* Copyright (c) 2024 Samsung Electronics Co., Ltd.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+*/
+
+// CLASS HEADER
+#include <dali/internal/graphics/vulkan-impl/vulkan-spirv.h>
+
+// INTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+
+// EXTERNAL EXCLUDES
+#include <utility>
+
+namespace Dali::Graphics::Vulkan
+{
+struct SPIRVGeneratorExtraInfo
+{
+  glslang_stage_t stage;
+  bool            valid;
+};
+
+SPIRVGenerator::SPIRVGenerator(Dali::Graphics::Vulkan::SPIRVGeneratorInfo generatorInfo)
+{
+  mGeneratorInfo = std::move(generatorInfo);
+
+  // Using new to inject internal data visible only within implementation
+  mGeneratorInfo.extraInfo = new SPIRVGeneratorExtraInfo;
+
+  mGeneratorInfo.extraInfo->valid = true;
+
+  switch(mGeneratorInfo.pipelineStage)
+  {
+    case PipelineStage::VERTEX_SHADER:
+    {
+      mGeneratorInfo.extraInfo->stage = GLSLANG_STAGE_VERTEX;
+      break;
+    }
+    case PipelineStage::FRAGMENT_SHADER:
+    {
+      mGeneratorInfo.extraInfo->stage = GLSLANG_STAGE_FRAGMENT;
+      break;
+    }
+    default:
+    {
+      DALI_LOG_ERROR("SPIRVGenerator: Unsupported stage used!\n");
+      mGeneratorInfo.extraInfo->valid = false;
+      return;
+    }
+  }
+}
+
+SPIRVGenerator::~SPIRVGenerator()
+{
+  delete mGeneratorInfo.extraInfo;
+}
+
+glslang_shader_t* SPIRVGenerator::CompileSource(glslang_stage_t stage, std::string shaderSource)
+{
+  const char*           cstr  = shaderSource.c_str();
+  const glslang_input_t input = {
+    .language                          = GLSLANG_SOURCE_GLSL,
+    .stage                             = stage,
+    .client                            = GLSLANG_CLIENT_VULKAN,
+    .client_version                    = GLSLANG_TARGET_VULKAN_1_2,
+    .target_language                   = GLSLANG_TARGET_SPV,
+    .target_language_version           = GLSLANG_TARGET_SPV_1_5,
+    .code                              = cstr,
+    .default_version                   = 100,
+    .default_profile                   = GLSLANG_NO_PROFILE,
+    .force_default_version_and_profile = false,
+    .forward_compatible                = false,
+    .messages                          = GLSLANG_MSG_DEFAULT_BIT,
+    .resource                          = glslang_default_resource(),
+  };
+
+  glslang_shader_t* shader = glslang_shader_create(&input);
+
+  if(!glslang_shader_preprocess(shader, &input))
+  {
+    DALI_LOG_ERROR("%s\n", glslang_shader_get_info_log(shader));
+    DALI_LOG_ERROR("%s\n", glslang_shader_get_info_debug_log(shader));
+    DALI_LOG_ERROR("%s\n", input.code);
+    glslang_shader_delete(shader);
+    return nullptr;
+  }
+
+  if(!glslang_shader_parse(shader, &input))
+  {
+    DALI_LOG_ERROR("%s\n", glslang_shader_get_info_log(shader));
+    DALI_LOG_ERROR("%s\n", glslang_shader_get_info_debug_log(shader));
+    DALI_LOG_ERROR("%s\n", glslang_shader_get_preprocessed_code(shader));
+    glslang_shader_delete(shader);
+    return nullptr;
+  }
+
+  return shader;
+}
+
+void SPIRVGenerator::Generate()
+{
+  glslang_initialize_process();
+  auto  stage  = mGeneratorInfo.extraInfo->stage;
+  auto* shader = CompileSource(stage, mGeneratorInfo.shaderCode);
+
+  if(!shader)
+  {
+    return;
+  }
+
+  glslang_program_t* program = glslang_program_create();
+  glslang_program_add_shader(program, shader);
+
+  if(!glslang_program_link(program, GLSLANG_MSG_SPV_RULES_BIT | GLSLANG_MSG_VULKAN_RULES_BIT))
+  {
+    DALI_LOG_ERROR("%s\n", glslang_program_get_info_log(program));
+    DALI_LOG_ERROR("%s\n", glslang_program_get_info_debug_log(program));
+    glslang_program_delete(program);
+    return;
+  }
+
+  glslang_program_SPIRV_generate(program, stage);
+
+  auto size = glslang_program_SPIRV_get_size(program);
+  mBinary.resize(size);
+  glslang_program_SPIRV_get(program, mBinary.data());
+
+  const char* spirv_messages = glslang_program_SPIRV_get_messages(program);
+  if(spirv_messages)
+    DALI_LOG_ERROR("%s\b", spirv_messages);
+
+  glslang_program_delete(program);
+  glslang_finalize_process();
+}
+
+bool SPIRVGenerator::IsValid() const
+{
+  return mGeneratorInfo.extraInfo->valid;
+}
+
+const std::vector<uint32_t>& SPIRVGenerator::Get()
+{
+  if(!mGeneratorInfo.extraInfo->valid)
+  {
+    mBinary.clear(); // return 0-size array
+  }
+
+  return mBinary;
+}
+
+} // namespace Dali::Graphics::Vulkan
\ No newline at end of file
diff --git a/dali/internal/graphics/vulkan-impl/vulkan-spirv.h b/dali/internal/graphics/vulkan-impl/vulkan-spirv.h
new file mode 100644 (file)
index 0000000..3af8094
--- /dev/null
@@ -0,0 +1,93 @@
+#ifndef DALI_GRAPHICS_VULKAN_SPIRV_H
+#define DALI_GRAPHICS_VULKAN_SPIRV_H
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/graphics-api/graphics-shader-create-info.h>
+#include <dali/graphics-api/graphics-shader.h>
+
+// INTERNAL INCLUDES
+#include <vector>
+#include "vulkan-graphics-resource.h"
+
+// EXTERNAL INCLUDES
+#include <glslang/Include/glslang_c_interface.h>
+#include <glslang/Public/resource_limits_c.h>
+
+namespace Dali::Graphics::Vulkan
+{
+class ShaderImpl;
+
+/**
+ * Info structure for generating SPIRV bytecode
+ */
+struct SPIRVGeneratorInfo
+{
+  std::string   shaderCode;
+  PipelineStage pipelineStage;
+
+  struct SPIRVGeneratorExtraInfo* extraInfo; ///< Reserved
+};
+
+/**
+ * Class generates SPIRV from GLSL
+ */
+class SPIRVGenerator
+{
+public:
+  /**
+   * @brief Constructor
+   * @param[in] generatorInfo valid SPIRVGeneratorInfo structure
+   */
+  explicit SPIRVGenerator(SPIRVGeneratorInfo generatorInfo);
+
+  /**
+   * @brief Destructor
+   */
+  ~SPIRVGenerator();
+
+  /**
+   * @brief Triggers compilation to SPIRV
+   */
+  void Generate();
+
+  /**
+   * @brief Checks whether generator is valid
+   * @return False if invalid
+   */
+  [[nodiscard]] bool IsValid() const;
+
+  /**
+   * @brief Returns SPIRV buffer
+   * @return Array of 32-bit words
+   */
+  const std::vector<uint32_t>& Get();
+
+private:
+  /**
+   * Compiles GLSL source to SPIRV
+   */
+  glslang_shader_t* CompileSource(glslang_stage_t stage, std::string shaderSource);
+
+  std::vector<uint32_t> mBinary;
+  SPIRVGeneratorInfo    mGeneratorInfo;
+};
+
+} // namespace Dali::Graphics::Vulkan
+#endif //DALI_GRAPHICS_VULKAN_SPIRV_H
\ No newline at end of file