blob: ed4559401c9ba58c28630e1b620fe34e17b0bf69 [file] [log] [blame]
// Copyright 2017 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "dawn/native/opengl/PipelineGL.h"
#include <algorithm>
#include <set>
#include <sstream>
#include <string>
#include "dawn/common/Range.h"
#include "dawn/native/BindGroupLayoutInternal.h"
#include "dawn/native/Device.h"
#include "dawn/native/Pipeline.h"
#include "dawn/native/opengl/BufferGL.h"
#include "dawn/native/opengl/DeviceGL.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"
#include "dawn/native/opengl/TextureGL.h"
#include "dawn/native/opengl/UtilsGL.h"
namespace dawn::native::opengl {
PipelineGL::PipelineGL() : mProgram(0) {}
PipelineGL::~PipelineGL() = default;
MaybeError PipelineGL::InitializeBase(const OpenGLFunctions& gl,
const PipelineLayout* layout,
const PerStage<ProgrammableStage>& stages,
ImmediateConstantMask& pipelineImmediateMask,
VertexAttributeMask bgraSwizzleAttributes,
Extent3D* workgroupSize) {
mProgram = DAWN_GL_TRY(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.
std::set<CombinedSampler> combinedSamplers;
mNeedsSSBOLengthUniformBuffer = false;
std::vector<GLuint> glShaders;
EmulatedTextureBuiltinRegistrar emulatedTextureBuiltins(layout);
for (SingleShaderStage stage : IterateStages(activeStages)) {
ShaderModule* module = ToBackend(stages[stage].module.Get());
bool needsSSBOLengthUniformBuffer = false;
std::vector<CombinedSampler> stageCombinedSamplers;
Extent3D localWorkgroupSize;
GLuint shader;
DAWN_TRY_ASSIGN(
shader, module->CompileShader(gl, stages[stage], stage, pipelineImmediateMask,
bgraSwizzleAttributes, &stageCombinedSamplers, layout,
&emulatedTextureBuiltins, &needsSSBOLengthUniformBuffer,
&localWorkgroupSize));
if (stage == SingleShaderStage::Compute) {
*workgroupSize = localWorkgroupSize;
}
mNeedsSSBOLengthUniformBuffer |= needsSSBOLengthUniformBuffer;
combinedSamplers.insert(stageCombinedSamplers.begin(), stageCombinedSamplers.end());
DAWN_GL_TRY(gl, AttachShader(mProgram, shader));
glShaders.push_back(shader);
}
mEmulatedTextureBuiltinInfo = emulatedTextureBuiltins.AcquireInfo();
// Link all the shaders together.
DAWN_GL_TRY(gl, LinkProgram(mProgram));
GLint linkStatus = GL_FALSE;
DAWN_GL_TRY(gl, GetProgramiv(mProgram, GL_LINK_STATUS, &linkStatus));
if (linkStatus == GL_FALSE) {
GLint infoLogLength = 0;
DAWN_GL_TRY(gl, GetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &infoLogLength));
if (infoLogLength > 1) {
std::vector<char> buffer(infoLogLength);
DAWN_GL_TRY(gl, GetProgramInfoLog(mProgram, infoLogLength, nullptr, &buffer[0]));
return DAWN_VALIDATION_ERROR("Program link failed:\n%s", buffer.data());
}
}
// Compute links between stages for combined samplers, then bind them to texture units
DAWN_GL_TRY(gl, UseProgram(mProgram));
const auto& indices = layout->GetBindingIndexInfo();
mUnitsForSamplers.resize(layout->GetNumSamplers());
mUnitsForTextures.resize(layout->GetNumSampledTextures());
// Assign combined texture/samplers to GL texture units.
TextureUnit textureUnit{0};
for (const auto& combined : combinedSamplers) {
// All the texture/samplers of a binding_array are set in a single glUniform1iv, gather them
// all in this vector.
absl::InlinedVector<GLint, 1> uniformsToSet;
BindingIndex textureArrayStart = combined.textureLocation.index;
for (auto textureArrayElement : Range(combined.textureLocation.shaderArraySize)) {
FlatBindingIndex textureGLIndex =
indices[combined.textureLocation.group][textureArrayStart + textureArrayElement];
mUnitsForTextures[textureGLIndex].push_back(textureUnit);
// Record that the placeholder sampler must be set for this texture unit if no sampler
// is used in the shader.
if (!combined.samplerLocation) {
mPlaceholderSamplerUnits.push_back(textureUnit);
} else {
// Record that the sampler used in the shader must be set for this texture unit.
BindingIndex samplerBindingIndex = combined.samplerLocation->index;
FlatBindingIndex samplerGLIndex =
indices[combined.samplerLocation->group][samplerBindingIndex];
mUnitsForSamplers[samplerGLIndex].push_back(textureUnit);
}
uniformsToSet.push_back(GLint(textureUnit));
textureUnit++;
}
std::string name = combined.GetName();
GLint location = DAWN_GL_TRY(gl, GetUniformLocation(mProgram, name.c_str()));
// Non-arrayed GLSL variables cannot be set with glUniform1iv
if (uniformsToSet.size() == 1) {
DAWN_GL_TRY(gl, Uniform1i(location, uniformsToSet[0]));
} else {
DAWN_GL_TRY(gl, Uniform1iv(location, uniformsToSet.size(), uniformsToSet.data()));
}
}
if (!mPlaceholderSamplerUnits.empty()) {
Ref<SamplerBase> sampler;
DAWN_TRY_ASSIGN(sampler, layout->GetDevice()->CreateSampler());
mPlaceholderSampler = ToBackend(std::move(sampler));
}
// If the pipeline declares immediates but the GL driver determines that they are unused and
// optimizes out the uniform variable, reset the mask. This prevents a GL_INVALID_VALUE error
// when trying to update it via glUniform*().
if (pipelineImmediateMask.any()) {
auto location = DAWN_GL_TRY(gl, GetUniformLocation(mProgram, "tint_immediates"));
if (location == -1) {
pipelineImmediateMask.reset();
}
}
for (GLuint glShader : glShaders) {
DAWN_GL_TRY(gl, DetachShader(mProgram, glShader));
DAWN_GL_TRY(gl, DeleteShader(glShader));
}
return {};
}
const std::vector<TextureUnit>& PipelineGL::GetTextureUnitsForSampler(
FlatBindingIndex index) const {
DAWN_ASSERT(index < mUnitsForSamplers.size());
return mUnitsForSamplers[index];
}
const std::vector<TextureUnit>& PipelineGL::GetTextureUnitsForTextureView(
FlatBindingIndex index) const {
DAWN_ASSERT(index < mUnitsForTextures.size());
return mUnitsForTextures[index];
}
MaybeError PipelineGL::ApplyNow(const OpenGLFunctions& gl, const PipelineLayout* layout) {
DAWN_GL_TRY(gl, UseProgram(mProgram));
for (TextureUnit unit : mPlaceholderSamplerUnits) {
DAWN_ASSERT(mPlaceholderSampler.Get() != nullptr);
DAWN_GL_TRY(gl, BindSampler(GLuint(unit), mPlaceholderSampler->GetHandle()));
}
return {};
}
const EmulatedTextureBuiltinInfo& PipelineGL::GetEmulatedTextureBuiltinInfo() const {
return mEmulatedTextureBuiltinInfo;
}
bool PipelineGL::NeedsTextureBuiltinUniformBuffer() const {
return !mEmulatedTextureBuiltinInfo.empty();
}
bool PipelineGL::NeedsSSBOLengthUniformBuffer() const {
return mNeedsSSBOLengthUniformBuffer;
}
// EmulatedTextureBuiltinRegistrar
EmulatedTextureBuiltinRegistrar::EmulatedTextureBuiltinRegistrar(const PipelineLayout* layout)
: mLayout(layout) {}
uint32_t EmulatedTextureBuiltinRegistrar::Register(BindGroupIndex group,
BindingIndex binding,
TextureQuery query) {
FlatBindingIndex firstTextureIndex = mLayout->GetBindingIndexInfo()[group][binding];
if (!mEmulatedTextureBuiltinInfo.contains(firstTextureIndex)) {
// Register the metadata to add for each element of the binding_array (if there is one).
BindingIndex arraySize =
mLayout->GetBindGroupLayout(group)->GetBindingInfo(binding).arraySize;
for (BindingIndex arrayElement : Range(arraySize)) {
FlatBindingIndex textureIndex =
mLayout->GetBindingIndexInfo()[group][binding + arrayElement];
mEmulatedTextureBuiltinInfo.emplace(
textureIndex,
EmulatedTextureBuiltin{.index = mCurrentIndex, .query = query, .group = group});
mCurrentIndex++;
}
}
return mEmulatedTextureBuiltinInfo[firstTextureIndex].index;
}
EmulatedTextureBuiltinInfo EmulatedTextureBuiltinRegistrar::AcquireInfo() {
return mEmulatedTextureBuiltinInfo;
}
} // namespace dawn::native::opengl