// 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 "common/BitSetIterator.h" #include "common/Log.h" #include "dawn_native/BindGroupLayout.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/ShaderModuleGL.h" #include namespace dawn_native { namespace 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; default: UNREACHABLE(); } } } // namespace PipelineGL::PipelineGL() { } void PipelineGL::Initialize(const OpenGLFunctions& gl, const PipelineLayout* layout, const PerStage& stages) { auto CreateShader = [](const OpenGLFunctions& gl, GLenum type, const char* source) -> 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 buffer(infoLogLength); gl.GetShaderInfoLog(shader, infoLogLength, nullptr, &buffer[0]); dawn::ErrorLog() << source << "\nProgram compilation failed:\n" << 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 combinedSamplers; for (SingleShaderStage stage : IterateStages(activeStages)) { const ShaderModule* module = ToBackend(stages[stage].module.Get()); std::string glsl = module->TranslateToGLSL(stages[stage].entryPoint.c_str(), stage, &combinedSamplers[stage]); GLuint shader = CreateShader(gl, GLShaderType(stage), glsl.c_str()); gl.AttachShader(mProgram, shader); } // 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 buffer(infoLogLength); gl.GetProgramInfoLog(mProgram, infoLogLength, nullptr, &buffer[0]); dawn::ErrorLog() << "Program link failed:\n" << buffer.data(); } } // The uniforms are part of the program state so we can pre-bind buffer units, texture units // etc. gl.UseProgram(mProgram); const auto& indices = layout->GetBindingIndexInfo(); for (BindGroupIndex group : IterateBitSet(layout->GetBindGroupLayoutsMask())) { const BindGroupLayoutBase* bgl = layout->GetBindGroupLayout(group); for (const auto& it : bgl->GetBindingMap()) { BindingNumber bindingNumber = it.first; BindingIndex bindingIndex = it.second; std::string name = GetBindingName(group, bindingNumber); switch (bgl->GetBindingInfo(bindingIndex).type) { case wgpu::BindingType::UniformBuffer: { GLint location = gl.GetUniformBlockIndex(mProgram, name.c_str()); if (location != -1) { gl.UniformBlockBinding(mProgram, location, indices[group][bindingIndex]); } break; } case wgpu::BindingType::StorageBuffer: case wgpu::BindingType::ReadonlyStorageBuffer: { GLuint location = gl.GetProgramResourceIndex( mProgram, GL_SHADER_STORAGE_BLOCK, name.c_str()); if (location != GL_INVALID_INDEX) { gl.ShaderStorageBlockBinding(mProgram, location, indices[group][bindingIndex]); } break; } case wgpu::BindingType::Sampler: case wgpu::BindingType::ComparisonSampler: case wgpu::BindingType::SampledTexture: case wgpu::BindingType::MultisampledTexture: // These binding types are handled in the separate sampler and texture // emulation break; case wgpu::BindingType::ReadonlyStorageTexture: case wgpu::BindingType::WriteonlyStorageTexture: { GLint location = gl.GetUniformLocation(mProgram, name.c_str()); if (location != -1) { gl.Uniform1i(location, indices[group][bindingIndex]); } break; } } } } // Compute links between stages for combined samplers, then bind them to texture units { std::set 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) { 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); Format::Type componentType = bgl->GetBindingInfo(bindingIndex).textureComponentType; shouldUseFiltering = componentType == Format::Type::Float; } { 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++; } } } const std::vector& PipelineGL::GetTextureUnitsForSampler( GLuint index) const { ASSERT(index < mUnitsForSamplers.size()); return mUnitsForSamplers[index]; } const std::vector& 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); } }} // namespace dawn_native::opengl