void CGraphicsContext::InternalDrawTeardown(SharedPointer<IPipelineState> State) { SharedPointer<GL::CPipelineState> PipelineState = std::dynamic_pointer_cast<GL::CPipelineState>(State); if (PipelineState->DrawWireframe) { CheckedGLCall(glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)); } if (PipelineState->CullFront || PipelineState->CullBack) { CheckedGLCall(glDisable(GL_CULL_FACE)); CheckedGLCall(glCullFace(GL_BACK)); // Default value } if (PipelineState->DisableDepthTest) { CheckedGLCall(glEnable(GL_DEPTH_TEST)); } if (PipelineState->DisableDepthWrite) { CheckedGLCall(glDepthMask(GL_TRUE)); } if (PipelineState->BlendMode != EBlendMode::None) { CheckedGLCall(glDisable(GL_BLEND)); } int TextureIndex = 0; for (auto const & it : PipelineState->BoundTextures) { CheckedGLCall(glActiveTexture(GL_TEXTURE0 + TextureIndex++)); SharedPointer<CTexture const> Texture = std::dynamic_pointer_cast<CTexture const>(it.second); CheckedGLCall(glBindTexture(Texture->GetGLBindTextureTarget(), 0)); } CheckedGLCall(glBindVertexArray(0)); }
void CShader::SetPixelStage(SharedPointer<IPixelStage> PixelShader) { if (PixelShader) { CheckedGLCall(glAttachShader(Handle, std::dynamic_pointer_cast<CPixelStage>(PixelShader)->Handle)); } }
void CShader::SetGeometryStage(SharedPointer<IGeometryStage> GeometryShader) { if (GeometryShader) { CheckedGLCall(glAttachShader(Handle, std::dynamic_pointer_cast<CGeometryStage>(GeometryShader)->Handle)); } }
void CShader::SetVertexStage(SharedPointer<IVertexStage> VertexShader) { if (VertexShader) { CheckedGLCall(glAttachShader(Handle, std::dynamic_pointer_cast<CVertexStage>(VertexShader)->Handle)); } }
void CShader::Link() { CheckedGLCall(glBindFragDataLocation(Handle, 0, "outColor")); CheckedGLCall(glLinkProgram(Handle)); int LinkStatus; CheckedGLCall(glGetProgramiv(Handle, GL_LINK_STATUS, & LinkStatus)); if (LinkStatus) { Linked = true; // Load active uniforms int ActiveUniforms = 0; int ActiveUniformMaxLength = 0; CheckedGLCall(glGetProgramiv(Handle, GL_ACTIVE_UNIFORMS, & ActiveUniforms)); CheckedGLCall(glGetProgramiv(Handle, GL_ACTIVE_UNIFORM_MAX_LENGTH, & ActiveUniformMaxLength)); for (int i = 0; i < ActiveUniforms; ++ i) { int Length = -1, Size = -1; uint DataType; char * Name = new char[ActiveUniformMaxLength + 1](); CheckedGLCall(glGetActiveUniform(Handle, i, ActiveUniformMaxLength, & Length, & Size, & DataType, Name)); Uniforms[Name] = glGetUniformLocation(Handle, Name); delete[] Name; } // Load active attributes int ActiveAttributes = 0; int ActiveAttributeMaxLength = 0; CheckedGLCall(glGetProgramiv(Handle, GL_ACTIVE_ATTRIBUTES, & ActiveAttributes)); CheckedGLCall(glGetProgramiv(Handle, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, & ActiveAttributeMaxLength)); for (GLint i = 0; i < ActiveAttributes; ++ i) { int Length = -1, Size = -1; uint DataType; char * Name = new char[ActiveAttributeMaxLength + 1]; CheckedGLCall(glGetActiveAttrib(Handle, i, ActiveAttributeMaxLength, & Length, & Size, & DataType, Name)); Attributes[Name] = std::make_pair(glGetAttribLocation(Handle, Name), DataType); delete[] Name; } } else { Log::Error("Failed to link shader program."); PrintProgramInfoLog(Handle); } }
void CGraphicsContext::DrawInstanced(SharedPointer<IPipelineState> State, uint const InstanceCount) { Window->MakeContextCurrent(); InternalDrawSetup(State); SharedPointer<GL::CPipelineState> PipelineState = std::dynamic_pointer_cast<GL::CPipelineState>(State); CheckedGLCall(glDrawElementsInstanced(GL_TRIANGLES, (int) PipelineState->IndexBuffer->Size, GL_UNSIGNED_INT, 0, InstanceCount)); InternalDrawTeardown(State); }
void CTexture::ApplyParams() { static u32 const FilterMatrix[3][2] = { {GL_NEAREST, GL_LINEAR}, {GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST}, {GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR}, }; static u32 const FilterLookup[2] = { GL_NEAREST, GL_LINEAR }; static u32 const WrapLookup[3] = { GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT, GL_REPEAT }; int MipMapMode; if (MipMaps) MipMapMode = 1 + (int) MipMapFilter; else MipMapMode = 0; int PreviouslyBoundTexture; CheckedGLCall(glGetIntegerv(GetGLTextureBindingEnum(), & PreviouslyBoundTexture)); CheckedGLCall(glBindTexture(GetGLBindTextureTarget(), Handle)); CheckedGLCall(glTexParameteri(GetGLBindTextureTarget(), GL_TEXTURE_MIN_FILTER, FilterMatrix[MipMapMode][(int) MinFilter])); CheckedGLCall(glTexParameteri(GetGLBindTextureTarget(), GL_TEXTURE_MAG_FILTER, FilterLookup[(int) MagFilter])); CheckedGLCall(glTexParameteri(GetGLBindTextureTarget(), GL_TEXTURE_WRAP_S, WrapLookup[(int) WrapMode])); CheckedGLCall(glTexParameteri(GetGLBindTextureTarget(), GL_TEXTURE_WRAP_T, WrapLookup[(int) WrapMode])); CheckedGLCall(glTexParameteri(GetGLBindTextureTarget(), GL_TEXTURE_WRAP_R, WrapLookup[(int) WrapMode])); float LargestAnisotropy = 2.f; CheckedGLCall(glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, & LargestAnisotropy)); if (Anisotropy < 0.f) Anisotropy = LargestAnisotropy; CheckedGLCall(glTexParameterf(GetGLBindTextureTarget(), GL_TEXTURE_MAX_ANISOTROPY_EXT, Clamp(Anisotropy, 0.f, LargestAnisotropy))); CheckedGLCall(glBindTexture(GetGLBindTextureTarget(), PreviouslyBoundTexture)); }
void CTextureCubeMap::UploadSubRegion(EFace const Face, void const * const Data, vec2u const & Offset, vec2u const & Size, EFormatComponents const Components, EScalarType const Type) { CheckedGLCall(glBindTexture(GL_TEXTURE_CUBE_MAP, Handle)); CheckExistingErrors(Texture2D::SubImage); glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + (int) Face, 0, Offset.X, Offset.Y, Size.X, Size.Y, FormatMatrix[(int) Components], Util::ScalarTypeMatrix[(int) Type], Data); if (OpenGLError()) { cerr << "Error occured during glTexSubImage2D for CTextureCubeMap: " << GetOpenGLError() << endl; cerr << "Handle is " << Handle << endl; cerr << "Offset is " << Offset << endl; cerr << "Size is " << Size << endl; cerr << "Format is " << FormatStringMatrix[(int) Components] << endl; cerr << "Type is " << Util::ScalarTypeStringMatrix[(int) Type] << endl; cerr << endl; } else { if (MipMaps) CheckedGLCall(glGenerateMipmap(GL_TEXTURE_CUBE_MAP)); ApplyParams(); } CheckedGLCall(glBindTexture(GL_TEXTURE_CUBE_MAP, 0)); }
void CPipelineState::Load() { if (! ShaderProgram || VertexBuffers.empty() || ! IndexBuffer) { Log::Error("Attempting to load an invalid PipelineState"); return; } Window->MakeContextCurrent(); CheckedGLCall(glUseProgram(ShaderProgram->Handle)); CheckedGLCall(glBindVertexArray(VertexArrayHandle)); for (auto & VertexBuffer : VertexBuffers) { CheckedGLCall(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer->Handle)); ////////////////////////////// // Set up VBOs (attributes) // ////////////////////////////// // Calculate stride of VBO data size_t TotalStride = 0; for (auto & InputLayoutElement : VertexBuffer->InputLayout) { TotalStride += GetAttributeTypeSize(InputLayoutElement.Type) * InputLayoutElement.Components; } size_t CurrentOffset = 0; for (auto & InputLayoutElement : VertexBuffer->InputLayout) { pair<uint, uint> AttributeInfo; if (TryMapAccess(ShaderProgram->Attributes, InputLayoutElement.Name, AttributeInfo)) { uint const AttributeLocation = AttributeInfo.first; uint const AttributeType = AttributeInfo.second; // Validate Attribute Type (does the VBO layout match what the shader wants?) { bool IsAttributeTypeCorrect = false; string ShaderAttributeTypeString = "Unknown"; switch (AttributeType) { default: Log::Error("Unexpected type for attribute %s: %u", InputLayoutElement.Name, AttributeType); break; case GL_FLOAT: IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 1); ShaderAttributeTypeString = "GL_FLOAT"; break; case GL_FLOAT_VEC2: IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 2); ShaderAttributeTypeString = "GL_FLOAT_VEC2"; break; case GL_FLOAT_VEC3: IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 3); ShaderAttributeTypeString = "GL_FLOAT_VEC3"; break; case GL_FLOAT_VEC4: IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Float && InputLayoutElement.Components == 4); ShaderAttributeTypeString = "GL_FLOAT_VEC4"; break; case GL_INT: IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 1); ShaderAttributeTypeString = "GL_INT"; break; case GL_INT_VEC2: IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 2); ShaderAttributeTypeString = "GL_INT_VEC2"; break; case GL_INT_VEC3: IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 3); ShaderAttributeTypeString = "GL_INT_VEC3"; break; case GL_INT_VEC4: IsAttributeTypeCorrect = (InputLayoutElement.Type == EAttributeType::Int && InputLayoutElement.Components == 4); ShaderAttributeTypeString = "GL_INT_VEC4"; break; } if (! IsAttributeTypeCorrect) { Log::Error("Mistmatch for attribute type '%s': VBO supplied %d components of type %s but shader expected '%s'", InputLayoutElement.Name, InputLayoutElement.Components, GetAttributeTypeString(InputLayoutElement.Type), ShaderAttributeTypeString); } } CheckedGLCall(glEnableVertexAttribArray(AttributeLocation)); switch (AttributeType) { case GL_FLOAT: case GL_FLOAT_VEC2: case GL_FLOAT_VEC3: case GL_FLOAT_VEC4: CheckedGLCall(glVertexAttribPointer( AttributeLocation, InputLayoutElement.Components, GetAttributeTypeOpenGLEnum(InputLayoutElement.Type), GL_FALSE, (int) TotalStride, (void *) CurrentOffset)); break; case GL_INT: case GL_INT_VEC2: case GL_INT_VEC3: case GL_INT_VEC4: CheckedGLCall(glVertexAttribIPointer( AttributeLocation, InputLayoutElement.Components, GetAttributeTypeOpenGLEnum(InputLayoutElement.Type), (int) TotalStride, (void *) CurrentOffset)); break; } if (VertexBuffer->Instancing) { CheckedGLCall(glVertexAttribDivisor(AttributeLocation, 1)); } UnboundAttributes.erase(InputLayoutElement.Name); } CurrentOffset += GetAttributeTypeSize(InputLayoutElement.Type) * InputLayoutElement.Components; } CheckedGLCall(glBindBuffer(GL_ARRAY_BUFFER, 0)); // Remember, VBOs are not part of VAO state (that's why we never leave them set in the VAO) } std::for_each(UnboundAttributes.begin(), UnboundAttributes.end(), [](string const & Attribuite) { Log::Error("Attribute expected by shader but not provided by VBO: %s", Attribuite); }); CheckedGLCall(glBindVertexArray(0)); CheckedGLCall(glUseProgram(0)); ///////////////////// // Set up Uniforms // ///////////////////// BoundUniforms.clear(); for (auto const & it : Uniforms) { uint Handle = 0; if (TryMapAccess(ShaderProgram->Uniforms, it.first, Handle)) { BoundUniforms[Handle] = it.second; } } for (auto const & it : Textures) { uint Handle = 0; if (TryMapAccess(ShaderProgram->Uniforms, it.first, Handle)) { BoundTextures[Handle] = it.second; } } std::for_each(UnboundUniforms.begin(), UnboundUniforms.end(), [](string const & Uniform) { Log::Error("Uniform expected by shader but not provided by PSO: %s", Uniform); }); Loaded = true; }
void CGraphicsContext::InternalDrawSetup(SharedPointer<IPipelineState> State) { SharedPointer<GL::CPipelineState> PipelineState = std::dynamic_pointer_cast<GL::CPipelineState>(State); if (! PipelineState->Loaded) { PipelineState->Load(); } if (! PipelineState->ShaderProgram) { Log::Error("Cannot draw pipeline state with no shader program."); return; } CheckedGLCall(glUseProgram(PipelineState->ShaderProgram->Handle)); CheckedGLCall(glBindVertexArray(PipelineState->VertexArrayHandle)); // Uniforms for (auto const & it : PipelineState->BoundUniforms) { switch (it.second->GetType()) { case EUniformType::Float: CheckedGLCall(glUniform1f(it.first, * static_cast<float const *>(it.second->GetData()))); break; case EUniformType::FloatArray: CheckedGLCall(glUniform1fv(it.first, (GLsizei) static_cast<vector<float> const *>(it.second->GetData())->size(), static_cast<vector<float> const *>(it.second->GetData())->data() )); break; case EUniformType::Float2: CheckedGLCall(glUniform2f(it.first, static_cast<SVectorBase<float, 2> const *>(it.second->GetData())->Values[0], static_cast<SVectorBase<float, 2> const *>(it.second->GetData())->Values[1])); break; case EUniformType::Float2Array: { vector<float> CompactedData; for (auto const & Vector : *static_cast<vector<vec2f> const *>(it.second->GetData())) { CompactedData.push_back(Vector.X); CompactedData.push_back(Vector.Y); } CheckedGLCall(glUniform2fv(it.first, (GLsizei) CompactedData.size() / 2, CompactedData.data() )); break; } case EUniformType::Float3: CheckedGLCall(glUniform3f(it.first, static_cast<SVectorBase<float, 3> const *>(it.second->GetData())->Values[0], static_cast<SVectorBase<float, 3> const *>(it.second->GetData())->Values[1], static_cast<SVectorBase<float, 3> const *>(it.second->GetData())->Values[2])); break; case EUniformType::Float3Array: { vector<float> CompactedData; for (auto const & Vector : *static_cast<vector<vec3f> const *>(it.second->GetData())) { CompactedData.push_back(Vector.X); CompactedData.push_back(Vector.Y); CompactedData.push_back(Vector.Z); } CheckedGLCall(glUniform3fv(it.first, (GLsizei) CompactedData.size() / 3, CompactedData.data() )); break; } case EUniformType::Float4: CheckedGLCall(glUniform4f(it.first, static_cast<SVectorBase<float, 4> const *>(it.second->GetData())->Values[0], static_cast<SVectorBase<float, 4> const *>(it.second->GetData())->Values[1], static_cast<SVectorBase<float, 4> const *>(it.second->GetData())->Values[2], static_cast<SVectorBase<float, 4> const *>(it.second->GetData())->Values[3])); break; case EUniformType::Matrix4x4: CheckedGLCall(glUniformMatrix4fv(it.first, 1, GL_FALSE, glm::value_ptr(* static_cast<glm::mat4 const *>(it.second->GetData())))); break; case EUniformType::Matrix4x4Array: { vector<float> CompactedData; for (auto const & Matrix : *static_cast<vector<glm::mat4> const *>(it.second->GetData())) { CompactedData.insert(CompactedData.end(), glm::value_ptr(Matrix), glm::value_ptr(Matrix) + 16); } CheckedGLCall(glUniformMatrix4fv(it.first, (GLsizei) CompactedData.size() / 16, GL_FALSE, CompactedData.data())); break; } case EUniformType::Int: CheckedGLCall(glUniform1i(it.first, * static_cast<uint const *>(it.second->GetData()))); break; case EUniformType::Int2: CheckedGLCall(glUniform2i(it.first, static_cast<SVectorBase<int, 2> const *>(it.second->GetData())->Values[0], static_cast<SVectorBase<int, 2> const *>(it.second->GetData())->Values[1])); break; case EUniformType::Int3: CheckedGLCall(glUniform3i(it.first, static_cast<SVectorBase<int, 3> const *>(it.second->GetData())->Values[0], static_cast<SVectorBase<int, 3> const *>(it.second->GetData())->Values[1], static_cast<SVectorBase<int, 3> const *>(it.second->GetData())->Values[2])); break; case EUniformType::Int4: CheckedGLCall(glUniform4i(it.first, static_cast<SVectorBase<int, 4> const *>(it.second->GetData())->Values[0], static_cast<SVectorBase<int, 4> const *>(it.second->GetData())->Values[1], static_cast<SVectorBase<int, 4> const *>(it.second->GetData())->Values[2], static_cast<SVectorBase<int, 4> const *>(it.second->GetData())->Values[3])); break; default: Log::Error("Unexpected uniform type during uniform binding: '%s'", GetUniformTypeString(it.second->GetType())); break; } } // Textures int TextureIndex = 0; for (auto const & it : PipelineState->BoundTextures) { CheckedGLCall(glUniform1i(it.first, TextureIndex)); CheckedGLCall(glActiveTexture(GL_TEXTURE0 + TextureIndex++)); SharedPointer<CTexture const> Texture = std::dynamic_pointer_cast<CTexture const>(it.second); CheckedGLCall(glBindTexture(Texture->GetGLBindTextureTarget(), Texture->Handle)); } // Draw Features if (PipelineState->DrawWireframe) { CheckedGLCall(glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)); } if (PipelineState->CullFront || PipelineState->CullBack) { glEnable(GL_CULL_FACE); if (! PipelineState->CullFront) CheckedGLCall(glCullFace(GL_BACK)); else if (! PipelineState->CullBack) CheckedGLCall(glCullFace(GL_FRONT)); else CheckedGLCall(glCullFace(GL_FRONT_AND_BACK)); } if (PipelineState->DisableDepthTest) { CheckedGLCall(glDisable(GL_DEPTH_TEST)); } if (PipelineState->DisableDepthWrite) { CheckedGLCall(glDepthMask(GL_FALSE)); } if (PipelineState->BlendMode != EBlendMode::None) { CheckedGLCall(glEnable(GL_BLEND)); if (PipelineState->BlendMode == EBlendMode::Alpha) { CheckedGLCall(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); } else if (PipelineState->BlendMode == EBlendMode::Additive) { CheckedGLCall(glBlendFunc(GL_SRC_ALPHA, GL_ONE)); } } }