| // Copyright 2017 The NXT 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 "PipelineGL.h" | |
| #include "OpenGLBackend.h" | |
| #include "PipelineLayoutGL.h" | |
| #include "ShaderModuleGL.h" | |
| #include <iostream> | |
| #include <set> | |
| namespace backend { | |
| namespace opengl { | |
| namespace { | |
| GLenum GLShaderType(nxt::ShaderStage stage) { | |
| switch (stage) { | |
| case nxt::ShaderStage::Vertex: | |
| return GL_VERTEX_SHADER; | |
| case nxt::ShaderStage::Fragment: | |
| return GL_FRAGMENT_SHADER; | |
| case nxt::ShaderStage::Compute: | |
| return GL_COMPUTE_SHADER; | |
| } | |
| } | |
| } | |
| Pipeline::Pipeline(Device* device, PipelineBuilder* builder) : PipelineBase(builder), device(device) { | |
| auto CreateShader = [](GLenum type, const char* source) -> GLuint { | |
| GLuint shader = glCreateShader(type); | |
| glShaderSource(shader, 1, &source, nullptr); | |
| glCompileShader(shader); | |
| GLint compileStatus = GL_FALSE; | |
| glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); | |
| if (compileStatus == GL_FALSE) { | |
| GLint infoLogLength = 0; | |
| glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength); | |
| if (infoLogLength > 1) { | |
| std::vector<char> buffer(infoLogLength); | |
| glGetShaderInfoLog(shader, infoLogLength, nullptr, &buffer[0]); | |
| std::cout << source << std::endl; | |
| std::cout << "Program compilation failed:\n"; | |
| std::cout << buffer.data() << std::endl; | |
| } | |
| } | |
| return shader; | |
| }; | |
| auto FillPushConstants = [](const ShaderModule* module, GLPushConstantInfo* info, GLuint program) { | |
| const auto& moduleInfo = module->GetPushConstants(); | |
| for (uint32_t i = 0; i < moduleInfo.names.size(); i++) { | |
| (*info)[i] = -1; | |
| unsigned int size = moduleInfo.sizes[i]; | |
| if (size == 0) { | |
| continue; | |
| } | |
| GLint location = glGetUniformLocation(program, moduleInfo.names[i].c_str()); | |
| if (location == -1) { | |
| continue; | |
| } | |
| for (uint32_t offset = 0; offset < size; offset++) { | |
| (*info)[i + offset] = location + offset; | |
| } | |
| i += size - 1; | |
| } | |
| }; | |
| program = glCreateProgram(); | |
| for (auto stage : IterateStages(GetStageMask())) { | |
| const ShaderModule* module = ToBackend(builder->GetStageInfo(stage).module.Get()); | |
| GLuint shader = CreateShader(GLShaderType(stage), module->GetSource()); | |
| glAttachShader(program, shader); | |
| } | |
| glLinkProgram(program); | |
| GLint linkStatus = GL_FALSE; | |
| glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); | |
| if (linkStatus == GL_FALSE) { | |
| GLint infoLogLength = 0; | |
| glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength); | |
| if (infoLogLength > 1) { | |
| std::vector<char> buffer(infoLogLength); | |
| glGetProgramInfoLog(program, infoLogLength, nullptr, &buffer[0]); | |
| std::cout << "Program link failed:\n"; | |
| std::cout << buffer.data() << std::endl; | |
| } | |
| } | |
| for (auto stage : IterateStages(GetStageMask())) { | |
| const ShaderModule* module = ToBackend(builder->GetStageInfo(stage).module.Get()); | |
| FillPushConstants(module, &glPushConstants[stage], program); | |
| } | |
| glUseProgram(program); | |
| // The uniforms are part of the program state so we can pre-bind buffer units, texture units etc. | |
| const auto& layout = ToBackend(GetLayout()); | |
| const auto& indices = layout->GetBindingIndexInfo(); | |
| for (uint32_t group = 0; group < kMaxBindGroups; ++group) { | |
| const auto& groupInfo = layout->GetBindGroupLayout(group)->GetBindingInfo(); | |
| for (uint32_t binding = 0; binding < kMaxBindingsPerGroup; ++binding) { | |
| if (!groupInfo.mask[binding]) { | |
| continue; | |
| } | |
| std::string name = GetBindingName(group, binding); | |
| switch (groupInfo.types[binding]) { | |
| case nxt::BindingType::UniformBuffer: | |
| { | |
| GLint location = glGetUniformBlockIndex(program, name.c_str()); | |
| glUniformBlockBinding(program, location, indices[group][binding]); | |
| } | |
| break; | |
| case nxt::BindingType::StorageBuffer: | |
| { | |
| GLuint location = glGetProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, name.c_str()); | |
| glShaderStorageBlockBinding(program, location, indices[group][binding]); | |
| } | |
| break; | |
| case nxt::BindingType::Sampler: | |
| case nxt::BindingType::SampledTexture: | |
| // These binding types are handled in the separate sampler and texture emulation | |
| break; | |
| } | |
| } | |
| } | |
| // Compute links between stages for combined samplers, then bind them to texture units | |
| { | |
| std::set<CombinedSampler> combinedSamplersSet; | |
| for (auto stage : IterateStages(GetStageMask())) { | |
| const auto& module = ToBackend(builder->GetStageInfo(stage).module); | |
| for (const auto& combined : module->GetCombinedSamplerInfo()) { | |
| combinedSamplersSet.insert(combined); | |
| } | |
| } | |
| unitsForSamplers.resize(layout->GetNumSamplers()); | |
| unitsForTextures.resize(layout->GetNumSampledTextures()); | |
| GLuint textureUnit = layout->GetTextureUnitsUsed(); | |
| for (const auto& combined : combinedSamplersSet) { | |
| std::string name = combined.GetName(); | |
| GLint location = glGetUniformLocation(program, name.c_str()); | |
| glUniform1i(location, textureUnit); | |
| GLuint samplerIndex = indices[combined.samplerLocation.group][combined.samplerLocation.binding]; | |
| unitsForSamplers[samplerIndex].push_back(textureUnit); | |
| GLuint textureIndex = indices[combined.textureLocation.group][combined.textureLocation.binding]; | |
| unitsForTextures[textureIndex].push_back(textureUnit); | |
| textureUnit ++; | |
| } | |
| } | |
| } | |
| const Pipeline::GLPushConstantInfo& Pipeline::GetGLPushConstants(nxt::ShaderStage stage) const { | |
| return glPushConstants[stage]; | |
| } | |
| const std::vector<GLuint>& Pipeline::GetTextureUnitsForSampler(GLuint index) const { | |
| ASSERT(index >= 0 && index < unitsForSamplers.size()); | |
| return unitsForSamplers[index]; | |
| } | |
| const std::vector<GLuint>& Pipeline::GetTextureUnitsForTexture(GLuint index) const { | |
| ASSERT(index >= 0 && index < unitsForSamplers.size()); | |
| return unitsForTextures[index]; | |
| } | |
| GLuint Pipeline::GetProgramHandle() const { | |
| return program; | |
| } | |
| void Pipeline::ApplyNow() { | |
| glUseProgram(program); | |
| auto inputState = ToBackend(GetInputState()); | |
| glBindVertexArray(inputState->GetVAO()); | |
| auto depthStencilState = ToBackend(GetDepthStencilState()); | |
| depthStencilState->ApplyNow(); | |
| } | |
| } | |
| } |