text: // Copyright 2017 The Dawn Authors
  //
  // 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.
  
  #include "dawn/native/opengl/PipelineGL.h"
  
  #include <set>
  #include <sstream>
  #include <string>
  
  #include "dawn/common/BitSetIterator.h"
  #include "dawn/native/BindGroupLayout.h"
  #include "dawn/native/Device.h"
  #include "dawn/native/Pipeline.h"
  #include "dawn/native/opengl/Forward.h"
  #include "dawn/native/opengl/OpenGLFunctions.h"
  #include "dawn/native/opengl/PipelineLayoutGL.h"
  #include "dawn/native/opengl/SamplerGL.h"
  #include "dawn/native/opengl/ShaderModuleGL.h"
  
  namespace dawn::native::opengl {
  
  namespace {
  
  GLenum GLShaderType(SingleShaderStage stage) {
      switch (stage) {
          case SingleShaderStage::Vertex:
              return GL_VERTEX_SHADER;
          case SingleShaderStage::Fragment:
              return GL_FRAGMENT_SHADER;
          case SingleShaderStage::Compute:
              return GL_COMPUTE_SHADER;
      }
      UNREACHABLE();
  }
  
  }  // namespace
  
  PipelineGL::PipelineGL() : mProgram(0) {}
  
  PipelineGL::~PipelineGL() = default;
  
  MaybeError PipelineGL::InitializeBase(const OpenGLFunctions& gl,
                                        const PipelineLayout* layout,
                                        const PerStage<ProgrammableStage>& stages) {
      auto CreateShader = [](const OpenGLFunctions& gl, GLenum type,
                             const char* source) -> ResultOrError<GLuint> {
          GLuint shader = gl.CreateShader(type);
          gl.ShaderSource(shader, 1, &source, nullptr);
          gl.CompileShader(shader);
  
          GLint compileStatus = GL_FALSE;
          gl.GetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus);
          if (compileStatus == GL_FALSE) {
              GLint infoLogLength = 0;
              gl.GetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
  
              if (infoLogLength > 1) {
                  std::vector<char> buffer(infoLogLength);
                  gl.GetShaderInfoLog(shader, infoLogLength, nullptr, &buffer[0]);
                  return DAWN_FORMAT_VALIDATION_ERROR("%s\nProgram compilation failed:\n%s", source,
                                                      buffer.data());
              }
          }
          return shader;
      };
  
      mProgram = gl.CreateProgram();
  
      // Compute the set of active stages.
      wgpu::ShaderStage activeStages = wgpu::ShaderStage::None;
      for (SingleShaderStage stage : IterateStages(kAllStages)) {
          if (stages[stage].module != nullptr) {
              activeStages |= StageBit(stage);
          }
      }
  
      // Create an OpenGL shader for each stage and gather the list of combined samplers.
      PerStage<CombinedSamplerInfo> combinedSamplers;
      bool needsPlaceholderSampler = false;
      std::vector<GLuint> glShaders;
      for (SingleShaderStage stage : IterateStages(activeStages)) {
          const ShaderModule* module = ToBackend(stages[stage].module.Get());
          std::string glsl;
          DAWN_TRY_ASSIGN(glsl, module->TranslateToGLSL(stages[stage].entryPoint.c_str(), stage,
                                                        &combinedSamplers[stage], layout,
                                                        &needsPlaceholderSampler));
          GLuint shader;
          DAWN_TRY_ASSIGN(shader, CreateShader(gl, GLShaderType(stage), glsl.c_str()));
          gl.AttachShader(mProgram, shader);
          glShaders.push_back(shader);
      }
  
      if (needsPlaceholderSampler) {
          SamplerDescriptor desc = {};
          ASSERT(desc.minFilter == wgpu::FilterMode::Nearest);
          ASSERT(desc.magFilter == wgpu::FilterMode::Nearest);
          ASSERT(desc.mipmapFilter == wgpu::FilterMode::Nearest);
          mPlaceholderSampler =
              ToBackend(layout->GetDevice()->GetOrCreateSampler(&desc).AcquireSuccess());
      }
  
      // Link all the shaders together.
      gl.LinkProgram(mProgram);
  
      GLint linkStatus = GL_FALSE;
      gl.GetProgramiv(mProgram, GL_LINK_STATUS, &linkStatus);
      if (linkStatus == GL_FALSE) {
          GLint infoLogLength = 0;
          gl.GetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &infoLogLength);
  
          if (infoLogLength > 1) {
              std::vector<char> buffer(infoLogLength);
              gl.GetProgramInfoLog(mProgram, infoLogLength, nullptr, &buffer[0]);
              return DAWN_FORMAT_VALIDATION_ERROR("Program link failed:\n%s", buffer.data());
          }
      }
  
      // Compute links between stages for combined samplers, then bind them to texture units
      gl.UseProgram(mProgram);
      const auto& indices = layout->GetBindingIndexInfo();
  
      std::set<CombinedSampler> combinedSamplersSet;
      for (SingleShaderStage stage : IterateStages(activeStages)) {
          for (const CombinedSampler& combined : combinedSamplers[stage]) {
              combinedSamplersSet.insert(combined);
          }
      }
  
      mUnitsForSamplers.resize(layout->GetNumSamplers());
      mUnitsForTextures.resize(layout->GetNumSampledTextures());
  
      GLuint textureUnit = layout->GetTextureUnitsUsed();
      for (const auto& combined : combinedSamplersSet) {
          const std::string& name = combined.GetName();
          GLint location = gl.GetUniformLocation(mProgram, name.c_str());
  
          if (location == -1) {
              continue;
          }
  
          gl.Uniform1i(location, textureUnit);
  
          bool shouldUseFiltering;
          {
              const BindGroupLayoutBase* bgl =
                  layout->GetBindGroupLayout(combined.textureLocation.group);
              BindingIndex bindingIndex = bgl->GetBindingIndex(combined.textureLocation.binding);
  
              GLuint textureIndex = indices[combined.textureLocation.group][bindingIndex];
              mUnitsForTextures[textureIndex].push_back(textureUnit);
  
              shouldUseFiltering = bgl->GetBindingInfo(bindingIndex).texture.sampleType ==
                                   wgpu::TextureSampleType::Float;
          }
          {
              if (combined.usePlaceholderSampler) {
                  mPlaceholderSamplerUnits.push_back(textureUnit);
              } else {
                  const BindGroupLayoutBase* bgl =
                      layout->GetBindGroupLayout(combined.samplerLocation.group);
                  BindingIndex bindingIndex = bgl->GetBindingIndex(combined.samplerLocation.binding);
  
                  GLuint samplerIndex = indices[combined.samplerLocation.group][bindingIndex];
                  mUnitsForSamplers[samplerIndex].push_back({textureUnit, shouldUseFiltering});
              }
          }
  
          textureUnit++;
      }
  
      for (GLuint glShader : glShaders) {
          gl.DetachShader(mProgram, glShader);
          gl.DeleteShader(glShader);
      }
  
      return {};
  }
  
  void PipelineGL::DeleteProgram(const OpenGLFunctions& gl) {
      gl.DeleteProgram(mProgram);
  }
  
  const std::vector<PipelineGL::SamplerUnit>& PipelineGL::GetTextureUnitsForSampler(
      GLuint index) const {
      ASSERT(index < mUnitsForSamplers.size());
      return mUnitsForSamplers[index];
  }
  
  const std::vector<GLuint>& PipelineGL::GetTextureUnitsForTextureView(GLuint index) const {
      ASSERT(index < mUnitsForTextures.size());
      return mUnitsForTextures[index];
  }
  
  GLuint PipelineGL::GetProgramHandle() const {
      return mProgram;
  }
  
  void PipelineGL::ApplyNow(const OpenGLFunctions& gl) {
      gl.UseProgram(mProgram);
      for (GLuint unit : mPlaceholderSamplerUnits) {
          ASSERT(mPlaceholderSampler.Get() != nullptr);
          gl.BindSampler(unit, mPlaceholderSampler->GetNonFilteringHandle());
      }
  }
  
  }  // namespace dawn::native::opengl
  