1 // Copyright 2017 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "dawn_native/opengl/PipelineGL.h" 16 17 #include "common/BitSetIterator.h" 18 #include "dawn_native/BindGroupLayout.h" 19 #include "dawn_native/Device.h" 20 #include "dawn_native/Pipeline.h" 21 #include "dawn_native/opengl/Forward.h" 22 #include "dawn_native/opengl/OpenGLFunctions.h" 23 #include "dawn_native/opengl/PipelineLayoutGL.h" 24 #include "dawn_native/opengl/SamplerGL.h" 25 #include "dawn_native/opengl/ShaderModuleGL.h" 26 27 #include <set> 28 #include <sstream> 29 30 namespace dawn_native { namespace opengl { 31 32 namespace { 33 GLShaderType(SingleShaderStage stage)34 GLenum GLShaderType(SingleShaderStage stage) { 35 switch (stage) { 36 case SingleShaderStage::Vertex: 37 return GL_VERTEX_SHADER; 38 case SingleShaderStage::Fragment: 39 return GL_FRAGMENT_SHADER; 40 case SingleShaderStage::Compute: 41 return GL_COMPUTE_SHADER; 42 } 43 UNREACHABLE(); 44 } 45 46 } // namespace 47 PipelineGL()48 PipelineGL::PipelineGL() : mProgram(0) { 49 } 50 51 PipelineGL::~PipelineGL() = default; 52 InitializeBase(const OpenGLFunctions & gl,const PipelineLayout * layout,const PerStage<ProgrammableStage> & stages)53 MaybeError PipelineGL::InitializeBase(const OpenGLFunctions& gl, 54 const PipelineLayout* layout, 55 const PerStage<ProgrammableStage>& stages) { 56 auto CreateShader = [](const OpenGLFunctions& gl, GLenum type, 57 const char* source) -> ResultOrError<GLuint> { 58 GLuint shader = gl.CreateShader(type); 59 gl.ShaderSource(shader, 1, &source, nullptr); 60 gl.CompileShader(shader); 61 62 GLint compileStatus = GL_FALSE; 63 gl.GetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); 64 if (compileStatus == GL_FALSE) { 65 GLint infoLogLength = 0; 66 gl.GetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength); 67 68 if (infoLogLength > 1) { 69 std::vector<char> buffer(infoLogLength); 70 gl.GetShaderInfoLog(shader, infoLogLength, nullptr, &buffer[0]); 71 return DAWN_FORMAT_VALIDATION_ERROR("%s\nProgram compilation failed:\n%s", 72 source, buffer.data()); 73 } 74 } 75 return shader; 76 }; 77 78 mProgram = gl.CreateProgram(); 79 80 // Compute the set of active stages. 81 wgpu::ShaderStage activeStages = wgpu::ShaderStage::None; 82 for (SingleShaderStage stage : IterateStages(kAllStages)) { 83 if (stages[stage].module != nullptr) { 84 activeStages |= StageBit(stage); 85 } 86 } 87 88 // Create an OpenGL shader for each stage and gather the list of combined samplers. 89 PerStage<CombinedSamplerInfo> combinedSamplers; 90 bool needsDummySampler = false; 91 std::vector<GLuint> glShaders; 92 for (SingleShaderStage stage : IterateStages(activeStages)) { 93 const ShaderModule* module = ToBackend(stages[stage].module.Get()); 94 std::string glsl; 95 DAWN_TRY_ASSIGN(glsl, module->TranslateToGLSL(stages[stage].entryPoint.c_str(), stage, 96 &combinedSamplers[stage], layout, 97 &needsDummySampler)); 98 GLuint shader; 99 DAWN_TRY_ASSIGN(shader, CreateShader(gl, GLShaderType(stage), glsl.c_str())); 100 gl.AttachShader(mProgram, shader); 101 glShaders.push_back(shader); 102 } 103 104 if (needsDummySampler) { 105 SamplerDescriptor desc = {}; 106 ASSERT(desc.minFilter == wgpu::FilterMode::Nearest); 107 ASSERT(desc.magFilter == wgpu::FilterMode::Nearest); 108 ASSERT(desc.mipmapFilter == wgpu::FilterMode::Nearest); 109 mDummySampler = 110 ToBackend(layout->GetDevice()->GetOrCreateSampler(&desc).AcquireSuccess()); 111 } 112 113 // Link all the shaders together. 114 gl.LinkProgram(mProgram); 115 116 GLint linkStatus = GL_FALSE; 117 gl.GetProgramiv(mProgram, GL_LINK_STATUS, &linkStatus); 118 if (linkStatus == GL_FALSE) { 119 GLint infoLogLength = 0; 120 gl.GetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &infoLogLength); 121 122 if (infoLogLength > 1) { 123 std::vector<char> buffer(infoLogLength); 124 gl.GetProgramInfoLog(mProgram, infoLogLength, nullptr, &buffer[0]); 125 return DAWN_FORMAT_VALIDATION_ERROR("Program link failed:\n%s", buffer.data()); 126 } 127 } 128 129 // Compute links between stages for combined samplers, then bind them to texture units 130 gl.UseProgram(mProgram); 131 const auto& indices = layout->GetBindingIndexInfo(); 132 133 std::set<CombinedSampler> combinedSamplersSet; 134 for (SingleShaderStage stage : IterateStages(activeStages)) { 135 for (const CombinedSampler& combined : combinedSamplers[stage]) { 136 combinedSamplersSet.insert(combined); 137 } 138 } 139 140 mUnitsForSamplers.resize(layout->GetNumSamplers()); 141 mUnitsForTextures.resize(layout->GetNumSampledTextures()); 142 143 GLuint textureUnit = layout->GetTextureUnitsUsed(); 144 for (const auto& combined : combinedSamplersSet) { 145 const std::string& name = combined.GetName(); 146 GLint location = gl.GetUniformLocation(mProgram, name.c_str()); 147 148 if (location == -1) { 149 continue; 150 } 151 152 gl.Uniform1i(location, textureUnit); 153 154 bool shouldUseFiltering; 155 { 156 const BindGroupLayoutBase* bgl = 157 layout->GetBindGroupLayout(combined.textureLocation.group); 158 BindingIndex bindingIndex = bgl->GetBindingIndex(combined.textureLocation.binding); 159 160 GLuint textureIndex = indices[combined.textureLocation.group][bindingIndex]; 161 mUnitsForTextures[textureIndex].push_back(textureUnit); 162 163 shouldUseFiltering = bgl->GetBindingInfo(bindingIndex).texture.sampleType == 164 wgpu::TextureSampleType::Float; 165 } 166 { 167 if (combined.useDummySampler) { 168 mDummySamplerUnits.push_back(textureUnit); 169 } else { 170 const BindGroupLayoutBase* bgl = 171 layout->GetBindGroupLayout(combined.samplerLocation.group); 172 BindingIndex bindingIndex = 173 bgl->GetBindingIndex(combined.samplerLocation.binding); 174 175 GLuint samplerIndex = indices[combined.samplerLocation.group][bindingIndex]; 176 mUnitsForSamplers[samplerIndex].push_back({textureUnit, shouldUseFiltering}); 177 } 178 } 179 180 textureUnit++; 181 } 182 183 for (GLuint glShader : glShaders) { 184 gl.DetachShader(mProgram, glShader); 185 gl.DeleteShader(glShader); 186 } 187 188 return {}; 189 } 190 DeleteProgram(const OpenGLFunctions & gl)191 void PipelineGL::DeleteProgram(const OpenGLFunctions& gl) { 192 gl.DeleteProgram(mProgram); 193 } 194 GetTextureUnitsForSampler(GLuint index) const195 const std::vector<PipelineGL::SamplerUnit>& PipelineGL::GetTextureUnitsForSampler( 196 GLuint index) const { 197 ASSERT(index < mUnitsForSamplers.size()); 198 return mUnitsForSamplers[index]; 199 } 200 GetTextureUnitsForTextureView(GLuint index) const201 const std::vector<GLuint>& PipelineGL::GetTextureUnitsForTextureView(GLuint index) const { 202 ASSERT(index < mUnitsForTextures.size()); 203 return mUnitsForTextures[index]; 204 } 205 GetProgramHandle() const206 GLuint PipelineGL::GetProgramHandle() const { 207 return mProgram; 208 } 209 ApplyNow(const OpenGLFunctions & gl)210 void PipelineGL::ApplyNow(const OpenGLFunctions& gl) { 211 gl.UseProgram(mProgram); 212 for (GLuint unit : mDummySamplerUnits) { 213 ASSERT(mDummySampler.Get() != nullptr); 214 gl.BindSampler(unit, mDummySampler->GetNonFilteringHandle()); 215 } 216 } 217 218 }} // namespace dawn_native::opengl 219