• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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