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/RenderPipelineGL.h" 16 17 #include "dawn_native/opengl/DeviceGL.h" 18 #include "dawn_native/opengl/Forward.h" 19 #include "dawn_native/opengl/PersistentPipelineStateGL.h" 20 #include "dawn_native/opengl/UtilsGL.h" 21 22 namespace dawn_native { namespace opengl { 23 24 namespace { 25 GLPrimitiveTopology(wgpu::PrimitiveTopology primitiveTopology)26 GLenum GLPrimitiveTopology(wgpu::PrimitiveTopology primitiveTopology) { 27 switch (primitiveTopology) { 28 case wgpu::PrimitiveTopology::PointList: 29 return GL_POINTS; 30 case wgpu::PrimitiveTopology::LineList: 31 return GL_LINES; 32 case wgpu::PrimitiveTopology::LineStrip: 33 return GL_LINE_STRIP; 34 case wgpu::PrimitiveTopology::TriangleList: 35 return GL_TRIANGLES; 36 case wgpu::PrimitiveTopology::TriangleStrip: 37 return GL_TRIANGLE_STRIP; 38 } 39 UNREACHABLE(); 40 } 41 ApplyFrontFaceAndCulling(const OpenGLFunctions & gl,wgpu::FrontFace face,wgpu::CullMode mode)42 void ApplyFrontFaceAndCulling(const OpenGLFunctions& gl, 43 wgpu::FrontFace face, 44 wgpu::CullMode mode) { 45 // Note that we invert winding direction in OpenGL. Because Y axis is up in OpenGL, 46 // which is different from WebGPU and other backends (Y axis is down). 47 GLenum direction = (face == wgpu::FrontFace::CCW) ? GL_CW : GL_CCW; 48 gl.FrontFace(direction); 49 50 if (mode == wgpu::CullMode::None) { 51 gl.Disable(GL_CULL_FACE); 52 } else { 53 gl.Enable(GL_CULL_FACE); 54 55 GLenum cullMode = (mode == wgpu::CullMode::Front) ? GL_FRONT : GL_BACK; 56 gl.CullFace(cullMode); 57 } 58 } 59 GLBlendFactor(wgpu::BlendFactor factor,bool alpha)60 GLenum GLBlendFactor(wgpu::BlendFactor factor, bool alpha) { 61 switch (factor) { 62 case wgpu::BlendFactor::Zero: 63 return GL_ZERO; 64 case wgpu::BlendFactor::One: 65 return GL_ONE; 66 case wgpu::BlendFactor::Src: 67 return GL_SRC_COLOR; 68 case wgpu::BlendFactor::OneMinusSrc: 69 return GL_ONE_MINUS_SRC_COLOR; 70 case wgpu::BlendFactor::SrcAlpha: 71 return GL_SRC_ALPHA; 72 case wgpu::BlendFactor::OneMinusSrcAlpha: 73 return GL_ONE_MINUS_SRC_ALPHA; 74 case wgpu::BlendFactor::Dst: 75 return GL_DST_COLOR; 76 case wgpu::BlendFactor::OneMinusDst: 77 return GL_ONE_MINUS_DST_COLOR; 78 case wgpu::BlendFactor::DstAlpha: 79 return GL_DST_ALPHA; 80 case wgpu::BlendFactor::OneMinusDstAlpha: 81 return GL_ONE_MINUS_DST_ALPHA; 82 case wgpu::BlendFactor::SrcAlphaSaturated: 83 return GL_SRC_ALPHA_SATURATE; 84 case wgpu::BlendFactor::Constant: 85 return alpha ? GL_CONSTANT_ALPHA : GL_CONSTANT_COLOR; 86 case wgpu::BlendFactor::OneMinusConstant: 87 return alpha ? GL_ONE_MINUS_CONSTANT_ALPHA : GL_ONE_MINUS_CONSTANT_COLOR; 88 } 89 UNREACHABLE(); 90 } 91 GLBlendMode(wgpu::BlendOperation operation)92 GLenum GLBlendMode(wgpu::BlendOperation operation) { 93 switch (operation) { 94 case wgpu::BlendOperation::Add: 95 return GL_FUNC_ADD; 96 case wgpu::BlendOperation::Subtract: 97 return GL_FUNC_SUBTRACT; 98 case wgpu::BlendOperation::ReverseSubtract: 99 return GL_FUNC_REVERSE_SUBTRACT; 100 case wgpu::BlendOperation::Min: 101 return GL_MIN; 102 case wgpu::BlendOperation::Max: 103 return GL_MAX; 104 } 105 UNREACHABLE(); 106 } 107 ApplyColorState(const OpenGLFunctions & gl,ColorAttachmentIndex attachment,const ColorTargetState * state)108 void ApplyColorState(const OpenGLFunctions& gl, 109 ColorAttachmentIndex attachment, 110 const ColorTargetState* state) { 111 GLuint colorBuffer = static_cast<GLuint>(static_cast<uint8_t>(attachment)); 112 if (state->blend != nullptr) { 113 gl.Enablei(GL_BLEND, colorBuffer); 114 gl.BlendEquationSeparatei(colorBuffer, GLBlendMode(state->blend->color.operation), 115 GLBlendMode(state->blend->alpha.operation)); 116 gl.BlendFuncSeparatei(colorBuffer, 117 GLBlendFactor(state->blend->color.srcFactor, false), 118 GLBlendFactor(state->blend->color.dstFactor, false), 119 GLBlendFactor(state->blend->alpha.srcFactor, true), 120 GLBlendFactor(state->blend->alpha.dstFactor, true)); 121 } else { 122 gl.Disablei(GL_BLEND, colorBuffer); 123 } 124 gl.ColorMaski(colorBuffer, state->writeMask & wgpu::ColorWriteMask::Red, 125 state->writeMask & wgpu::ColorWriteMask::Green, 126 state->writeMask & wgpu::ColorWriteMask::Blue, 127 state->writeMask & wgpu::ColorWriteMask::Alpha); 128 } 129 ApplyColorState(const OpenGLFunctions & gl,const ColorTargetState * state)130 void ApplyColorState(const OpenGLFunctions& gl, const ColorTargetState* state) { 131 if (state->blend != nullptr) { 132 gl.Enable(GL_BLEND); 133 gl.BlendEquationSeparate(GLBlendMode(state->blend->color.operation), 134 GLBlendMode(state->blend->alpha.operation)); 135 gl.BlendFuncSeparate(GLBlendFactor(state->blend->color.srcFactor, false), 136 GLBlendFactor(state->blend->color.dstFactor, false), 137 GLBlendFactor(state->blend->alpha.srcFactor, true), 138 GLBlendFactor(state->blend->alpha.dstFactor, true)); 139 } else { 140 gl.Disable(GL_BLEND); 141 } 142 gl.ColorMask(state->writeMask & wgpu::ColorWriteMask::Red, 143 state->writeMask & wgpu::ColorWriteMask::Green, 144 state->writeMask & wgpu::ColorWriteMask::Blue, 145 state->writeMask & wgpu::ColorWriteMask::Alpha); 146 } 147 Equal(const BlendComponent & lhs,const BlendComponent & rhs)148 bool Equal(const BlendComponent& lhs, const BlendComponent& rhs) { 149 return lhs.operation == rhs.operation && lhs.srcFactor == rhs.srcFactor && 150 lhs.dstFactor == rhs.dstFactor; 151 } 152 OpenGLStencilOperation(wgpu::StencilOperation stencilOperation)153 GLuint OpenGLStencilOperation(wgpu::StencilOperation stencilOperation) { 154 switch (stencilOperation) { 155 case wgpu::StencilOperation::Keep: 156 return GL_KEEP; 157 case wgpu::StencilOperation::Zero: 158 return GL_ZERO; 159 case wgpu::StencilOperation::Replace: 160 return GL_REPLACE; 161 case wgpu::StencilOperation::Invert: 162 return GL_INVERT; 163 case wgpu::StencilOperation::IncrementClamp: 164 return GL_INCR; 165 case wgpu::StencilOperation::DecrementClamp: 166 return GL_DECR; 167 case wgpu::StencilOperation::IncrementWrap: 168 return GL_INCR_WRAP; 169 case wgpu::StencilOperation::DecrementWrap: 170 return GL_DECR_WRAP; 171 } 172 UNREACHABLE(); 173 } 174 ApplyDepthStencilState(const OpenGLFunctions & gl,const DepthStencilState * descriptor,PersistentPipelineState * persistentPipelineState)175 void ApplyDepthStencilState(const OpenGLFunctions& gl, 176 const DepthStencilState* descriptor, 177 PersistentPipelineState* persistentPipelineState) { 178 // Depth writes only occur if depth is enabled 179 if (descriptor->depthCompare == wgpu::CompareFunction::Always && 180 !descriptor->depthWriteEnabled) { 181 gl.Disable(GL_DEPTH_TEST); 182 } else { 183 gl.Enable(GL_DEPTH_TEST); 184 } 185 186 if (descriptor->depthWriteEnabled) { 187 gl.DepthMask(GL_TRUE); 188 } else { 189 gl.DepthMask(GL_FALSE); 190 } 191 192 gl.DepthFunc(ToOpenGLCompareFunction(descriptor->depthCompare)); 193 194 if (StencilTestEnabled(descriptor)) { 195 gl.Enable(GL_STENCIL_TEST); 196 } else { 197 gl.Disable(GL_STENCIL_TEST); 198 } 199 200 GLenum backCompareFunction = ToOpenGLCompareFunction(descriptor->stencilBack.compare); 201 GLenum frontCompareFunction = ToOpenGLCompareFunction(descriptor->stencilFront.compare); 202 persistentPipelineState->SetStencilFuncsAndMask( 203 gl, backCompareFunction, frontCompareFunction, descriptor->stencilReadMask); 204 205 gl.StencilOpSeparate(GL_BACK, OpenGLStencilOperation(descriptor->stencilBack.failOp), 206 OpenGLStencilOperation(descriptor->stencilBack.depthFailOp), 207 OpenGLStencilOperation(descriptor->stencilBack.passOp)); 208 gl.StencilOpSeparate(GL_FRONT, OpenGLStencilOperation(descriptor->stencilFront.failOp), 209 OpenGLStencilOperation(descriptor->stencilFront.depthFailOp), 210 OpenGLStencilOperation(descriptor->stencilFront.passOp)); 211 212 gl.StencilMask(descriptor->stencilWriteMask); 213 } 214 215 } // anonymous namespace 216 217 // static CreateUninitialized(Device * device,const RenderPipelineDescriptor * descriptor)218 Ref<RenderPipeline> RenderPipeline::CreateUninitialized( 219 Device* device, 220 const RenderPipelineDescriptor* descriptor) { 221 return AcquireRef(new RenderPipeline(device, descriptor)); 222 } 223 RenderPipeline(Device * device,const RenderPipelineDescriptor * descriptor)224 RenderPipeline::RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor) 225 : RenderPipelineBase(device, descriptor), 226 mVertexArrayObject(0), 227 mGlPrimitiveTopology(GLPrimitiveTopology(GetPrimitiveTopology())) { 228 } 229 Initialize()230 MaybeError RenderPipeline::Initialize() { 231 DAWN_TRY( 232 InitializeBase(ToBackend(GetDevice())->gl, ToBackend(GetLayout()), GetAllStages())); 233 CreateVAOForVertexState(); 234 return {}; 235 } 236 237 RenderPipeline::~RenderPipeline() = default; 238 DestroyImpl()239 void RenderPipeline::DestroyImpl() { 240 RenderPipelineBase::DestroyImpl(); 241 const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; 242 gl.DeleteVertexArrays(1, &mVertexArrayObject); 243 gl.BindVertexArray(0); 244 DeleteProgram(gl); 245 } 246 GetGLPrimitiveTopology() const247 GLenum RenderPipeline::GetGLPrimitiveTopology() const { 248 return mGlPrimitiveTopology; 249 } 250 251 ityp::bitset<VertexAttributeLocation, kMaxVertexAttributes> GetAttributesUsingVertexBuffer(VertexBufferSlot slot) const252 RenderPipeline::GetAttributesUsingVertexBuffer(VertexBufferSlot slot) const { 253 ASSERT(!IsError()); 254 return mAttributesUsingVertexBuffer[slot]; 255 } 256 CreateVAOForVertexState()257 void RenderPipeline::CreateVAOForVertexState() { 258 const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; 259 260 gl.GenVertexArrays(1, &mVertexArrayObject); 261 gl.BindVertexArray(mVertexArrayObject); 262 263 for (VertexAttributeLocation location : IterateBitSet(GetAttributeLocationsUsed())) { 264 const auto& attribute = GetAttribute(location); 265 GLuint glAttrib = static_cast<GLuint>(static_cast<uint8_t>(location)); 266 gl.EnableVertexAttribArray(glAttrib); 267 268 mAttributesUsingVertexBuffer[attribute.vertexBufferSlot][location] = true; 269 const VertexBufferInfo& vertexBuffer = GetVertexBuffer(attribute.vertexBufferSlot); 270 271 if (vertexBuffer.arrayStride == 0) { 272 // Emulate a stride of zero (constant vertex attribute) by 273 // setting the attribute instance divisor to a huge number. 274 gl.VertexAttribDivisor(glAttrib, 0xffffffff); 275 } else { 276 switch (vertexBuffer.stepMode) { 277 case wgpu::VertexStepMode::Vertex: 278 break; 279 case wgpu::VertexStepMode::Instance: 280 gl.VertexAttribDivisor(glAttrib, 1); 281 break; 282 } 283 } 284 } 285 } 286 ApplyNow(PersistentPipelineState & persistentPipelineState)287 void RenderPipeline::ApplyNow(PersistentPipelineState& persistentPipelineState) { 288 const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; 289 PipelineGL::ApplyNow(gl); 290 291 ASSERT(mVertexArrayObject); 292 gl.BindVertexArray(mVertexArrayObject); 293 294 ApplyFrontFaceAndCulling(gl, GetFrontFace(), GetCullMode()); 295 296 ApplyDepthStencilState(gl, GetDepthStencilState(), &persistentPipelineState); 297 298 gl.SampleMaski(0, GetSampleMask()); 299 if (IsAlphaToCoverageEnabled()) { 300 gl.Enable(GL_SAMPLE_ALPHA_TO_COVERAGE); 301 } else { 302 gl.Disable(GL_SAMPLE_ALPHA_TO_COVERAGE); 303 } 304 305 if (IsDepthBiasEnabled()) { 306 gl.Enable(GL_POLYGON_OFFSET_FILL); 307 float depthBias = GetDepthBias(); 308 float slopeScale = GetDepthBiasSlopeScale(); 309 if (gl.PolygonOffsetClamp != nullptr) { 310 gl.PolygonOffsetClamp(slopeScale, depthBias, GetDepthBiasClamp()); 311 } else { 312 gl.PolygonOffset(slopeScale, depthBias); 313 } 314 } else { 315 gl.Disable(GL_POLYGON_OFFSET_FILL); 316 } 317 318 if (!GetDevice()->IsToggleEnabled(Toggle::DisableIndexedDrawBuffers)) { 319 for (ColorAttachmentIndex attachmentSlot : IterateBitSet(GetColorAttachmentsMask())) { 320 ApplyColorState(gl, attachmentSlot, GetColorTargetState(attachmentSlot)); 321 } 322 } else { 323 const ColorTargetState* prevDescriptor = nullptr; 324 for (ColorAttachmentIndex attachmentSlot : IterateBitSet(GetColorAttachmentsMask())) { 325 const ColorTargetState* descriptor = GetColorTargetState(attachmentSlot); 326 if (!prevDescriptor) { 327 ApplyColorState(gl, descriptor); 328 prevDescriptor = descriptor; 329 } else if ((descriptor->blend == nullptr) != (prevDescriptor->blend == nullptr)) { 330 // TODO(crbug.com/dawn/582): GLES < 3.2 does not support different blend states 331 // per color target. Add validation to prevent this as it is not. 332 ASSERT(false); 333 } else if (descriptor->blend != nullptr) { 334 if (!Equal(descriptor->blend->alpha, prevDescriptor->blend->alpha) || 335 !Equal(descriptor->blend->color, prevDescriptor->blend->color) || 336 descriptor->writeMask != prevDescriptor->writeMask) { 337 // TODO(crbug.com/dawn/582) 338 ASSERT(false); 339 } 340 } 341 } 342 } 343 } 344 345 }} // namespace dawn_native::opengl 346