in renderdoc/driver/gl/gl_replay.cpp [832:1977]
void GLReplay::SavePipelineState(uint32_t eventId)
{
if(!m_GLPipelineState)
return;
GLPipe::State &pipe = *m_GLPipelineState;
WrappedOpenGL &drv = *m_pDriver;
GLResourceManager *rm = m_pDriver->GetResourceManager();
MakeCurrentReplayContext(&m_ReplayCtx);
GLRenderState rs;
rs.FetchState(&drv);
// Index buffer
ContextPair &ctx = drv.GetCtx();
pipe.descriptorStore = m_pDriver->m_DescriptorsID;
pipe.descriptorCount = EncodeGLDescriptorIndex({GLDescriptorMapping::Count, 0});
pipe.descriptorByteSize = 1;
m_Access.clear();
GLuint vao = 0;
drv.glGetIntegerv(eGL_VERTEX_ARRAY_BINDING, (GLint *)&vao);
pipe.vertexInput.vertexArrayObject = rm->GetOriginalID(rm->GetResID(VertexArrayRes(ctx, vao)));
GLuint ibuffer = 0;
drv.glGetIntegerv(eGL_ELEMENT_ARRAY_BUFFER_BINDING, (GLint *)&ibuffer);
pipe.vertexInput.indexBuffer = rm->GetOriginalID(rm->GetResID(BufferRes(ctx, ibuffer)));
pipe.vertexInput.primitiveRestart = rs.Enabled[GLRenderState::eEnabled_PrimitiveRestart] ||
rs.Enabled[GLRenderState::eEnabled_PrimitiveRestartFixedIndex];
pipe.vertexInput.restartIndex = rs.Enabled[GLRenderState::eEnabled_PrimitiveRestartFixedIndex]
? ~0U
: rs.PrimitiveRestartIndex;
const GLDrawParams &drawParams = m_pDriver->GetDrawParameters(eventId);
pipe.vertexInput.indexByteStride = drawParams.indexWidth;
pipe.vertexInput.topology = drawParams.topo;
// Vertex buffers and attributes
GLint numVBufferBindings = 16;
drv.glGetIntegerv(eGL_MAX_VERTEX_ATTRIB_BINDINGS, &numVBufferBindings);
GLint numVAttribBindings = 16;
drv.glGetIntegerv(eGL_MAX_VERTEX_ATTRIBS, &numVAttribBindings);
pipe.vertexInput.vertexBuffers.resize(numVBufferBindings);
pipe.vertexInput.attributes.resize(numVAttribBindings);
for(GLuint i = 0; i < (GLuint)numVBufferBindings; i++)
{
GLuint buffer = GetBoundVertexBuffer(i);
pipe.vertexInput.vertexBuffers[i].resourceId =
rm->GetOriginalID(rm->GetResID(BufferRes(ctx, buffer)));
drv.glGetIntegeri_v(eGL_VERTEX_BINDING_STRIDE, i,
(GLint *)&pipe.vertexInput.vertexBuffers[i].byteStride);
drv.glGetIntegeri_v(eGL_VERTEX_BINDING_OFFSET, i,
(GLint *)&pipe.vertexInput.vertexBuffers[i].byteOffset);
drv.glGetIntegeri_v(eGL_VERTEX_BINDING_DIVISOR, i,
(GLint *)&pipe.vertexInput.vertexBuffers[i].instanceDivisor);
}
for(GLuint i = 0; i < (GLuint)numVAttribBindings; i++)
{
drv.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_ENABLED,
(GLint *)&pipe.vertexInput.attributes[i].enabled);
drv.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_BINDING,
(GLint *)&pipe.vertexInput.attributes[i].vertexBufferSlot);
drv.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_RELATIVE_OFFSET,
(GLint *)&pipe.vertexInput.attributes[i].byteOffset);
GLenum type = eGL_FLOAT;
GLint normalized = 0;
drv.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_TYPE, (GLint *)&type);
drv.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &normalized);
GLint integer = 0;
drv.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_INTEGER, &integer);
RDCEraseEl(pipe.vertexInput.attributes[i].genericValue);
drv.glGetVertexAttribfv(i, eGL_CURRENT_VERTEX_ATTRIB,
(GLfloat *)pipe.vertexInput.attributes[i].genericValue.floatValue.data());
ResourceFormat fmt;
fmt.type = ResourceFormatType::Regular;
GLint compCount;
drv.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_SIZE, (GLint *)&compCount);
fmt.compCount = (uint8_t)compCount;
switch(type)
{
default:
case eGL_BYTE:
fmt.compByteWidth = 1;
fmt.compType = CompType::SInt;
break;
case eGL_UNSIGNED_BYTE:
fmt.compByteWidth = 1;
fmt.compType = CompType::UInt;
break;
case eGL_SHORT:
fmt.compByteWidth = 2;
fmt.compType = CompType::SInt;
break;
case eGL_UNSIGNED_SHORT:
fmt.compByteWidth = 2;
fmt.compType = CompType::UInt;
break;
case eGL_INT:
fmt.compByteWidth = 4;
fmt.compType = CompType::SInt;
break;
case eGL_UNSIGNED_INT:
fmt.compByteWidth = 4;
fmt.compType = CompType::UInt;
break;
case eGL_FLOAT:
fmt.compByteWidth = 4;
fmt.compType = CompType::Float;
break;
case eGL_DOUBLE:
fmt.compByteWidth = 8;
fmt.compType = CompType::Float;
break;
case eGL_HALF_FLOAT:
fmt.compByteWidth = 2;
fmt.compType = CompType::Float;
break;
case eGL_INT_2_10_10_10_REV:
fmt.type = ResourceFormatType::R10G10B10A2;
fmt.compCount = 4;
fmt.compType = CompType::SInt;
break;
case eGL_UNSIGNED_INT_2_10_10_10_REV:
fmt.type = ResourceFormatType::R10G10B10A2;
fmt.compCount = 4;
fmt.compType = CompType::UInt;
break;
case eGL_UNSIGNED_INT_10F_11F_11F_REV:
fmt.type = ResourceFormatType::R11G11B10;
fmt.compCount = 3;
fmt.compType = CompType::Float;
// spec says this format is never normalized regardless.
normalized = 0;
break;
}
if(compCount == eGL_BGRA)
{
fmt.compByteWidth = 1;
fmt.compCount = 4;
fmt.SetBGRAOrder(true);
fmt.compType = CompType::UNorm;
// spec says BGRA inputs are ALWAYS normalised
normalized = 1;
if(type == eGL_UNSIGNED_INT_2_10_10_10_REV || type == eGL_INT_2_10_10_10_REV)
{
fmt.type = ResourceFormatType::R10G10B10A2;
fmt.compType = type == eGL_UNSIGNED_INT_2_10_10_10_REV ? CompType::UInt : CompType::SInt;
}
else if(type != eGL_UNSIGNED_BYTE)
{
// haven't checked the other cases work properly
RDCERR("Unexpected BGRA type");
}
}
// normalized/floatCast flags are irrelevant for float formats
if(fmt.compType == CompType::SInt || fmt.compType == CompType::UInt)
{
// if it wasn't an integer, it's cast to float
pipe.vertexInput.attributes[i].floatCast = !integer;
// if we're casting, change the component type as appropriate
if(!integer)
{
if(normalized != 0)
fmt.compType = (fmt.compType == CompType::SInt) ? CompType::SNorm : CompType::UNorm;
}
}
else
{
pipe.vertexInput.attributes[i].floatCast = false;
}
pipe.vertexInput.attributes[i].format = fmt;
}
pipe.vertexInput.provokingVertexLast = (rs.ProvokingVertex != eGL_FIRST_VERTEX_CONVENTION);
pipe.vertexProcessing.defaultInnerLevel = rs.PatchParams.defaultInnerLevel;
pipe.vertexProcessing.defaultOuterLevel = rs.PatchParams.defaultOuterLevel;
pipe.vertexProcessing.discard = rs.Enabled[GLRenderState::eEnabled_RasterizerDiscard];
pipe.vertexProcessing.clipOriginLowerLeft = (rs.ClipOrigin != eGL_UPPER_LEFT);
pipe.vertexProcessing.clipNegativeOneToOne = (rs.ClipDepth != eGL_ZERO_TO_ONE);
for(int i = 0; i < 8; i++)
pipe.vertexProcessing.clipPlanes[i] = rs.Enabled[GLRenderState::eEnabled_ClipDistance0 + i];
// Shader stages & Textures
GLint numTexUnits = 8;
drv.glGetIntegerv(eGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &numTexUnits);
GLenum activeTexture = eGL_TEXTURE0;
drv.glGetIntegerv(eGL_ACTIVE_TEXTURE, (GLint *)&activeTexture);
pipe.vertexShader.stage = ShaderStage::Vertex;
pipe.tessControlShader.stage = ShaderStage::Tess_Control;
pipe.tessEvalShader.stage = ShaderStage::Tess_Eval;
pipe.geometryShader.stage = ShaderStage::Geometry;
pipe.fragmentShader.stage = ShaderStage::Fragment;
pipe.computeShader.stage = ShaderStage::Compute;
GLPipe::Shader *stages[NumShaderStages] = {
&pipe.vertexShader, &pipe.tessControlShader, &pipe.tessEvalShader,
&pipe.geometryShader, &pipe.fragmentShader, &pipe.computeShader,
};
ShaderReflection *refls[NumShaderStages] = {NULL};
ResourceId progIds[NumShaderStages];
ResourceId shadIds[NumShaderStages];
GLuint progForStage[NumShaderStages] = {};
bool spirv[NumShaderStages] = {false};
for(size_t i = 0; i < NumShaderStages; i++)
{
if(!stages[i])
continue;
stages[i]->programResourceId = stages[i]->shaderResourceId = ResourceId();
stages[i]->reflection = NULL;
}
rdcarray<int32_t> vertexAttrBindings;
{
GLuint curProg = 0;
drv.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint *)&curProg);
if(curProg == 0)
{
GLuint curPipe = 0;
drv.glGetIntegerv(eGL_PROGRAM_PIPELINE_BINDING, (GLint *)&curPipe);
if(curPipe != 0)
{
ResourceId id = rm->GetResID(ProgramPipeRes(ctx, curPipe));
const WrappedOpenGL::PipelineData &pipeDetails = m_pDriver->m_Pipelines[id];
pipe.pipelineResourceId = rm->GetUnreplacedOriginalID(id);
for(size_t i = 0; i < ARRAY_COUNT(pipeDetails.stageShaders); i++)
{
if(!stages[i])
continue;
if(pipeDetails.stageShaders[i] != ResourceId())
{
progIds[i] = pipeDetails.stagePrograms[i];
shadIds[i] = pipeDetails.stageShaders[i];
progForStage[i] = rm->GetCurrentResource(pipeDetails.stagePrograms[i]).name;
}
}
}
}
else
{
ResourceId id = rm->GetResID(ProgramRes(ctx, curProg));
const WrappedOpenGL::ProgramData &progDetails = m_pDriver->m_Programs[id];
pipe.pipelineResourceId = ResourceId();
for(size_t i = 0; i < ARRAY_COUNT(progDetails.stageShaders); i++)
{
if(!stages[i])
continue;
if(progDetails.stageShaders[i] != ResourceId())
{
progIds[i] = id;
shadIds[i] = progDetails.stageShaders[i];
progForStage[i] = curProg;
}
}
}
}
for(size_t i = 0; i < NumShaderStages; i++)
{
if(progForStage[i])
{
progForStage[i] = rm->GetCurrentResource(progIds[i]).name;
stages[i]->programResourceId = rm->GetUnreplacedOriginalID(progIds[i]);
stages[i]->shaderResourceId = rm->GetUnreplacedOriginalID(shadIds[i]);
const WrappedOpenGL::ShaderData &shaderDetails = m_pDriver->m_Shaders[shadIds[i]];
if(shaderDetails.reflection->resourceId == ResourceId())
stages[i]->reflection = refls[i] = NULL;
else
stages[i]->reflection = refls[i] = shaderDetails.reflection;
if(!shaderDetails.spirvWords.empty())
spirv[i] = true;
if(i == 0)
EvaluateVertexAttributeBinds(progForStage[i], refls[i], spirv[i], vertexAttrBindings);
}
else if(stages[i])
{
stages[i]->programResourceId = stages[i]->shaderResourceId = ResourceId();
stages[i]->reflection = NULL;
}
}
for(size_t i = 0; i < pipe.vertexInput.attributes.size(); i++)
{
if(i < vertexAttrBindings.size())
pipe.vertexInput.attributes[i].boundShaderInput = vertexAttrBindings[i];
else
pipe.vertexInput.attributes[i].boundShaderInput = -1;
}
pipe.textureCompleteness.clear();
for(size_t s = 0; s < NumShaderStages; s++)
{
ShaderReflection *refl = refls[s];
if(!refl)
continue;
GLuint prog = progForStage[s];
DescriptorAccess access;
access.descriptorStore = m_pDriver->m_DescriptorsID;
access.stage = refl->stage;
access.byteSize = 1;
m_Access.reserve(m_Access.size() + refl->constantBlocks.size() +
refl->readOnlyResources.size() + refl->readWriteResources.size());
RDCASSERT(refl->constantBlocks.size() < 0xffff, refl->constantBlocks.size());
for(uint16_t i = 0; i < refl->constantBlocks.size(); i++)
{
uint32_t slot = 0;
bool used = false;
GetCurrentBinding(prog, refl, refl->constantBlocks[i], slot, used);
access.staticallyUnused = !used;
access.type = DescriptorType::ConstantBuffer;
access.index = i;
if(!refl->constantBlocks[i].bufferBacked)
access.byteOffset = EncodeGLDescriptorIndex({GLDescriptorMapping::BareUniforms, (uint32_t)s});
else
access.byteOffset =
EncodeGLDescriptorIndex({GLDescriptorMapping::UniformBinding, (uint32_t)slot});
m_Access.push_back(access);
}
RDCASSERT(refl->readOnlyResources.size() < 0xffff, refl->readOnlyResources.size());
for(uint16_t i = 0; i < refl->readOnlyResources.size(); i++)
{
uint32_t slot = 0;
bool used = false;
GetCurrentBinding(prog, refl, refl->readOnlyResources[i], slot, used);
access.staticallyUnused = !used;
GLDescriptorMapping descType = GLDescriptorMapping::Tex2D;
switch(refl->readOnlyResources[i].textureType)
{
case TextureType::Buffer: descType = GLDescriptorMapping::TexBuffer; break;
case TextureType::Texture1D: descType = GLDescriptorMapping::Tex1D; break;
case TextureType::Texture1DArray: descType = GLDescriptorMapping::Tex1DArray; break;
case TextureType::Texture2D: descType = GLDescriptorMapping::Tex2D; break;
case TextureType::TextureRect: descType = GLDescriptorMapping::TexRect; break;
case TextureType::Texture2DArray: descType = GLDescriptorMapping::Tex2DArray; break;
case TextureType::Texture2DMS: descType = GLDescriptorMapping::Tex2DMS; break;
case TextureType::Texture2DMSArray: descType = GLDescriptorMapping::Tex2DMSArray; break;
case TextureType::Texture3D: descType = GLDescriptorMapping::Tex3D; break;
case TextureType::TextureCube: descType = GLDescriptorMapping::TexCube; break;
case TextureType::TextureCubeArray: descType = GLDescriptorMapping::TexCubeArray; break;
case TextureType::Unknown:
case TextureType::Count:
RDCERR("Invalid resource type on binding %s", refl->readOnlyResources[i].name.c_str());
break;
}
access.type = DescriptorType::ImageSampler;
if(descType == GLDescriptorMapping::TexBuffer)
access.type = DescriptorType::TypedBuffer;
access.index = i;
access.byteOffset = EncodeGLDescriptorIndex({descType, slot});
m_Access.push_back(access);
// checking texture completeness is a pretty expensive operation since it requires a lot of
// queries against the driver's texture properties.
// We assume that if a texture and sampler are complete at any point, even if their
// properties change mid-frame they will stay complete. Similarly if they are _incomplete_
// they will stay incomplete. Thus we can cache the results for a given pair, which if
// samplers don't change (or are only ever used consistently with the same texture) amounts
// to one entry per texture.
// Note that textures can't change target, so we don't need to icnlude the target in the key
drv.glActiveTexture(GLenum(eGL_TEXTURE0 + slot));
GLenum binding = eGL_NONE;
switch(refl->readOnlyResources[i].textureType)
{
case TextureType::Unknown: binding = eGL_NONE; break;
case TextureType::Buffer: binding = eGL_TEXTURE_BINDING_BUFFER; break;
case TextureType::Texture1D: binding = eGL_TEXTURE_BINDING_1D; break;
case TextureType::Texture1DArray: binding = eGL_TEXTURE_BINDING_1D_ARRAY; break;
case TextureType::Texture2D: binding = eGL_TEXTURE_BINDING_2D; break;
case TextureType::TextureRect: binding = eGL_TEXTURE_BINDING_RECTANGLE; break;
case TextureType::Texture2DArray: binding = eGL_TEXTURE_BINDING_2D_ARRAY; break;
case TextureType::Texture2DMS: binding = eGL_TEXTURE_BINDING_2D_MULTISAMPLE; break;
case TextureType::Texture2DMSArray:
binding = eGL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY;
break;
case TextureType::Texture3D: binding = eGL_TEXTURE_BINDING_3D; break;
case TextureType::TextureCube: binding = eGL_TEXTURE_BINDING_CUBE_MAP; break;
case TextureType::TextureCubeArray: binding = eGL_TEXTURE_BINDING_CUBE_MAP_ARRAY; break;
case TextureType::Count: RDCERR("Invalid shader resource type"); break;
}
GLuint tex = 0;
if(descType == GLDescriptorMapping::TexCubeArray && !HasExt[ARB_texture_cube_map_array])
tex = 0;
else
drv.glGetIntegerv(binding, (GLint *)&tex);
GLuint samp = 0;
if(HasExt[ARB_sampler_objects])
drv.glGetIntegerv(eGL_SAMPLER_BINDING, (GLint *)&samp);
CompleteCacheKey complete = {tex, samp};
auto it = m_CompleteCache.find(complete);
if(it == m_CompleteCache.end())
it = m_CompleteCache.insert(
it,
std::make_pair(complete, GetTextureCompleteStatus(TextureTarget(binding), tex, samp)));
if(!it->second.empty())
{
GLPipe::TextureCompleteness completeness;
completeness.descriptorByteOffset = access.byteOffset;
completeness.completeStatus = it->second;
pipe.textureCompleteness.push_back(completeness);
}
}
RDCASSERT(refl->readWriteResources.size() < 0xffff, refl->readWriteResources.size());
for(uint16_t i = 0; i < refl->readWriteResources.size(); i++)
{
uint32_t slot = 0;
bool used = false;
GetCurrentBinding(prog, refl, refl->readWriteResources[i], slot, used);
access.staticallyUnused = !used;
GLDescriptorMapping descType = GLDescriptorMapping::Images;
if(refl->readWriteResources[i].isTexture)
{
access.type = DescriptorType::ReadWriteImage;
if(refl->readWriteResources[i].textureType == TextureType::Buffer)
access.type = DescriptorType::ReadWriteTypedBuffer;
}
else
{
access.type = DescriptorType::ReadWriteBuffer;
descType = GLDescriptorMapping::ShaderStorage;
if(refl->readWriteResources[i].variableType.rows == 1 &&
refl->readWriteResources[i].variableType.columns == 1 &&
refl->readWriteResources[i].variableType.baseType == VarType::UInt)
{
descType = GLDescriptorMapping::AtomicCounter;
}
}
access.index = i;
access.byteOffset = EncodeGLDescriptorIndex({descType, slot});
m_Access.push_back(access);
}
}
// GL is ass-backwards in its handling of texture units. When a shader is active
// the types in the glsl samplers inform which targets are used from which texture units
//
// So texture unit 5 can have a 2D bound (texture 52) and a Cube bound (texture 77).
// * if a uniform sampler2D has value 5 then the 2D texture is used, and we sample from 52
// * if a uniform samplerCube has value 5 then the Cube texture is used, and we sample from 77
// It's illegal for both a sampler2D and samplerCube to both have the same value (or any two
// different types). It makes it all rather pointless and needlessly complex.
//
// What we have to do then, is consider the program, look at the values of the uniforms, and
// then check if two uniforms with different types point to the same binding
for(uint32_t unit = 0; unit < (uint32_t)numTexUnits; unit++)
{
rdcstr typeConflict;
GLenum binding = eGL_NONE;
GLenum target = eGL_NONE;
TextureType resType = TextureType::Unknown;
rdcstr firstBindName;
rdcarray<uint32_t> descriptorsReferenced;
for(const DescriptorAccess &access : m_Access)
{
// only look at read-only descriptors, these are the texture units that can clash
if(!IsReadOnlyDescriptor(access.type))
continue;
ShaderReflection *refl = refls[(uint32_t)access.stage];
if(refl == NULL)
{
RDCERR("Unexpected NULL reflection on %s shader with a descriptor access",
ToStr(access.stage).c_str());
continue;
}
uint32_t accessedUnit = DecodeGLDescriptorIndex(access.byteOffset).idx;
// accessed the same unit, check its binding
if(accessedUnit == unit)
{
if(!descriptorsReferenced.contains(access.byteOffset))
descriptorsReferenced.push_back(access.byteOffset);
const ShaderResource &res = refl->readOnlyResources[access.index];
GLenum t = eGL_NONE;
switch(res.textureType)
{
case TextureType::Unknown: target = eGL_NONE; break;
case TextureType::Buffer: target = eGL_TEXTURE_BUFFER; break;
case TextureType::Texture1D: target = eGL_TEXTURE_1D; break;
case TextureType::Texture1DArray: target = eGL_TEXTURE_1D_ARRAY; break;
case TextureType::Texture2D: target = eGL_TEXTURE_2D; break;
case TextureType::TextureRect: target = eGL_TEXTURE_RECTANGLE; break;
case TextureType::Texture2DArray: target = eGL_TEXTURE_2D_ARRAY; break;
case TextureType::Texture2DMS: target = eGL_TEXTURE_2D_MULTISAMPLE; break;
case TextureType::Texture2DMSArray: target = eGL_TEXTURE_2D_MULTISAMPLE_ARRAY; break;
case TextureType::Texture3D: target = eGL_TEXTURE_3D; break;
case TextureType::TextureCube: target = eGL_TEXTURE_CUBE_MAP; break;
case TextureType::TextureCubeArray: target = eGL_TEXTURE_CUBE_MAP_ARRAY; break;
case TextureType::Count: RDCERR("Invalid shader resource type"); break;
}
if(target != eGL_NONE)
t = TextureBinding(target);
if(binding == eGL_NONE)
{
binding = t;
firstBindName = res.name;
resType = res.textureType;
}
else if(binding == t)
{
// two uniforms with the same type pointing to the same slot is fine
binding = t;
}
else if(binding != t)
{
RDCERR("Two uniforms pointing to texture unit %d with types %s and %s", unit,
ToStr(binding).c_str(), ToStr(t).c_str());
if(typeConflict.empty())
{
typeConflict = StringFormat::Fmt("First binding found '%s' is %s",
firstBindName.c_str(), ToStr(resType).c_str());
}
typeConflict +=
StringFormat::Fmt(", '%s' is %s", res.name.c_str(), ToStr(res.textureType).c_str());
}
}
}
// if we found a type conflict, add an entry for all descriptors
if(!typeConflict.empty())
{
for(uint32_t descriptor : descriptorsReferenced)
{
bool found = false;
for(GLPipe::TextureCompleteness &completeness : pipe.textureCompleteness)
{
if(completeness.descriptorByteOffset == descriptor)
{
// don't worry about overwriting, the descriptor byte offset is unique to the unit so
// we should only set this at most once
completeness.typeConflict = typeConflict;
found = true;
}
}
if(!found)
{
GLPipe::TextureCompleteness completeness;
completeness.descriptorByteOffset = descriptor;
completeness.typeConflict = typeConflict;
pipe.textureCompleteness.push_back(completeness);
}
}
}
}
RDCEraseEl(pipe.transformFeedback);
if(HasExt[ARB_transform_feedback2])
{
GLuint feedback = 0;
drv.glGetIntegerv(eGL_TRANSFORM_FEEDBACK_BINDING, (GLint *)&feedback);
if(feedback != 0)
pipe.transformFeedback.feedbackResourceId =
rm->GetOriginalID(rm->GetResID(FeedbackRes(ctx, feedback)));
else
pipe.transformFeedback.feedbackResourceId = ResourceId();
GLint maxCount = 0;
drv.glGetIntegerv(eGL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &maxCount);
for(int i = 0; i < (int)ARRAY_COUNT(pipe.transformFeedback.bufferResourceId) && i < maxCount; i++)
{
GLuint buffer = 0;
drv.glGetIntegeri_v(eGL_TRANSFORM_FEEDBACK_BUFFER_BINDING, i, (GLint *)&buffer);
pipe.transformFeedback.bufferResourceId[i] =
rm->GetOriginalID(rm->GetResID(BufferRes(ctx, buffer)));
drv.glGetInteger64i_v(eGL_TRANSFORM_FEEDBACK_BUFFER_START, i,
(GLint64 *)&pipe.transformFeedback.byteOffset[i]);
drv.glGetInteger64i_v(eGL_TRANSFORM_FEEDBACK_BUFFER_SIZE, i,
(GLint64 *)&pipe.transformFeedback.byteSize[i]);
}
GLint p = 0;
drv.glGetIntegerv(eGL_TRANSFORM_FEEDBACK_BUFFER_PAUSED, &p);
pipe.transformFeedback.paused = (p != 0);
drv.glGetIntegerv(eGL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE, &p);
pipe.transformFeedback.active = (p != 0) || m_pDriver->m_WasActiveFeedback;
}
for(size_t i = 0; i < ARRAY_COUNT(rs.Subroutines); i++)
{
size_t num = RDCMIN(128, rs.Subroutines[i].numSubroutines);
if(num == 0)
{
RDCEraseEl(stages[i]->subroutines);
}
else
{
stages[i]->subroutines.resize(num);
memcpy(stages[i]->subroutines.data(), rs.Subroutines[i].Values, num * sizeof(uint32_t));
}
}
drv.glActiveTexture(activeTexture);
// Vertex post processing and rasterization
RDCCOMPILE_ASSERT(ARRAY_COUNT(rs.Viewports) == ARRAY_COUNT(rs.DepthRanges),
"GL Viewport count does not match depth ranges count");
pipe.rasterizer.viewports.resize(ARRAY_COUNT(rs.Viewports));
for(size_t v = 0; v < pipe.rasterizer.viewports.size(); ++v)
{
pipe.rasterizer.viewports[v].x = rs.Viewports[v].x;
pipe.rasterizer.viewports[v].y = rs.Viewports[v].y;
pipe.rasterizer.viewports[v].width = rs.Viewports[v].width;
pipe.rasterizer.viewports[v].height = rs.Viewports[v].height;
pipe.rasterizer.viewports[v].minDepth = (float)rs.DepthRanges[v].nearZ;
pipe.rasterizer.viewports[v].maxDepth = (float)rs.DepthRanges[v].farZ;
}
pipe.rasterizer.scissors.resize(ARRAY_COUNT(rs.Scissors));
for(size_t s = 0; s < pipe.rasterizer.scissors.size(); ++s)
{
pipe.rasterizer.scissors[s].x = rs.Scissors[s].x;
pipe.rasterizer.scissors[s].y = rs.Scissors[s].y;
pipe.rasterizer.scissors[s].width = rs.Scissors[s].width;
pipe.rasterizer.scissors[s].height = rs.Scissors[s].height;
pipe.rasterizer.scissors[s].enabled = rs.Scissors[s].enabled;
}
int polygonOffsetEnableEnum;
switch(rs.PolygonMode)
{
default:
RDCWARN("Unexpected value for POLYGON_MODE %x", rs.PolygonMode);
DELIBERATE_FALLTHROUGH();
case eGL_FILL:
pipe.rasterizer.state.fillMode = FillMode::Solid;
polygonOffsetEnableEnum = GLRenderState::eEnabled_PolyOffsetFill;
break;
case eGL_LINE:
pipe.rasterizer.state.fillMode = FillMode::Wireframe;
polygonOffsetEnableEnum = GLRenderState::eEnabled_PolyOffsetLine;
break;
case eGL_POINT:
pipe.rasterizer.state.fillMode = FillMode::Point;
polygonOffsetEnableEnum = GLRenderState::eEnabled_PolyOffsetPoint;
break;
}
if(rs.Enabled[polygonOffsetEnableEnum])
{
pipe.rasterizer.state.depthBias = rs.PolygonOffset[1];
pipe.rasterizer.state.slopeScaledDepthBias = rs.PolygonOffset[0];
pipe.rasterizer.state.offsetClamp = rs.PolygonOffset[2];
}
else
{
pipe.rasterizer.state.depthBias = 0.0f;
pipe.rasterizer.state.slopeScaledDepthBias = 0.0f;
pipe.rasterizer.state.offsetClamp = 0.0f;
}
if(rs.Enabled[GLRenderState::eEnabled_CullFace])
{
switch(rs.CullFace)
{
default: RDCWARN("Unexpected value for CULL_FACE %x", rs.CullFace); DELIBERATE_FALLTHROUGH();
case eGL_BACK: pipe.rasterizer.state.cullMode = CullMode::Back; break;
case eGL_FRONT: pipe.rasterizer.state.cullMode = CullMode::Front; break;
case eGL_FRONT_AND_BACK: pipe.rasterizer.state.cullMode = CullMode::FrontAndBack; break;
}
}
else
{
pipe.rasterizer.state.cullMode = CullMode::NoCull;
}
RDCASSERT(rs.FrontFace == eGL_CCW || rs.FrontFace == eGL_CW);
pipe.rasterizer.state.frontCCW = rs.FrontFace == eGL_CCW;
pipe.rasterizer.state.depthClamp = rs.Enabled[GLRenderState::eEnabled_DepthClamp];
pipe.rasterizer.state.multisampleEnable = rs.Enabled[GLRenderState::eEnabled_Multisample];
pipe.rasterizer.state.sampleShading = rs.Enabled[GLRenderState::eEnabled_SampleShading];
pipe.rasterizer.state.sampleMask = rs.Enabled[GLRenderState::eEnabled_SampleMask];
pipe.rasterizer.state.sampleMaskValue =
rs.SampleMask[0]; // assume number of samples is less than 32
pipe.rasterizer.state.sampleCoverage = rs.Enabled[GLRenderState::eEnabled_SampleCoverage];
pipe.rasterizer.state.sampleCoverageInvert = rs.SampleCoverageInvert;
pipe.rasterizer.state.sampleCoverageValue = rs.SampleCoverage;
pipe.rasterizer.state.alphaToCoverage = rs.Enabled[GLRenderState::eEnabled_SampleAlphaToCoverage];
pipe.rasterizer.state.alphaToOne = rs.Enabled[GLRenderState::eEnabled_SampleAlphaToOne];
pipe.rasterizer.state.minSampleShadingRate = rs.MinSampleShading;
pipe.rasterizer.state.programmablePointSize = rs.Enabled[rs.eEnabled_ProgramPointSize];
pipe.rasterizer.state.pointSize = rs.PointSize;
pipe.rasterizer.state.lineWidth = rs.LineWidth;
pipe.rasterizer.state.pointFadeThreshold = rs.PointFadeThresholdSize;
pipe.rasterizer.state.pointOriginUpperLeft = (rs.PointSpriteOrigin != eGL_LOWER_LEFT);
// depth and stencil states
pipe.depthState.depthEnable = rs.Enabled[GLRenderState::eEnabled_DepthTest];
pipe.depthState.depthWrites = rs.DepthWriteMask != 0;
pipe.depthState.depthFunction = MakeCompareFunc(rs.DepthFunc);
pipe.depthState.depthBounds = rs.Enabled[GLRenderState::eEnabled_DepthBoundsEXT];
pipe.depthState.nearBound = rs.DepthBounds.nearZ;
pipe.depthState.farBound = rs.DepthBounds.farZ;
pipe.stencilState.stencilEnable = rs.Enabled[GLRenderState::eEnabled_StencilTest];
pipe.stencilState.frontFace.compareMask = rs.StencilFront.valuemask;
pipe.stencilState.frontFace.writeMask = rs.StencilFront.writemask;
pipe.stencilState.frontFace.reference = uint8_t(rs.StencilFront.ref & 0xff);
pipe.stencilState.frontFace.function = MakeCompareFunc(rs.StencilFront.func);
pipe.stencilState.frontFace.passOperation = MakeStencilOp(rs.StencilFront.pass);
pipe.stencilState.frontFace.failOperation = MakeStencilOp(rs.StencilFront.stencilFail);
pipe.stencilState.frontFace.depthFailOperation = MakeStencilOp(rs.StencilFront.depthFail);
pipe.stencilState.backFace.compareMask = rs.StencilBack.valuemask;
pipe.stencilState.backFace.writeMask = rs.StencilBack.writemask;
pipe.stencilState.backFace.reference = uint8_t(rs.StencilBack.ref & 0xff);
pipe.stencilState.backFace.function = MakeCompareFunc(rs.StencilBack.func);
pipe.stencilState.backFace.passOperation = MakeStencilOp(rs.StencilBack.pass);
pipe.stencilState.backFace.failOperation = MakeStencilOp(rs.StencilBack.stencilFail);
pipe.stencilState.backFace.depthFailOperation = MakeStencilOp(rs.StencilBack.depthFail);
// Frame buffer
GLuint curDrawFBO = 0;
drv.glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, (GLint *)&curDrawFBO);
GLuint curReadFBO = 0;
drv.glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, (GLint *)&curReadFBO);
GLint numCols = 8;
drv.glGetIntegerv(eGL_MAX_COLOR_ATTACHMENTS, &numCols);
bool rbCol[32] = {false};
bool rbDepth = false;
bool rbStencil = false;
GLuint curCol[32] = {0};
GLuint curDepth = 0;
GLuint curStencil = 0;
RDCASSERT(numCols <= 32);
// we should never bind the true default framebuffer - if the app did, we will have our fake bound
RDCASSERT(curDrawFBO != 0);
RDCASSERT(curReadFBO != 0);
{
GLenum type = eGL_TEXTURE;
for(GLint i = 0; i < numCols; i++)
{
drv.glGetFramebufferAttachmentParameteriv(
eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + i),
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint *)&curCol[i]);
drv.glGetFramebufferAttachmentParameteriv(
eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + i),
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint *)&type);
if(type == eGL_RENDERBUFFER)
rbCol[i] = true;
}
drv.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
(GLint *)&curDepth);
drv.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint *)&type);
if(type == eGL_RENDERBUFFER)
rbDepth = true;
drv.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
(GLint *)&curStencil);
drv.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint *)&type);
if(type == eGL_RENDERBUFFER)
rbStencil = true;
pipe.framebuffer.drawFBO.resourceId =
rm->GetOriginalID(rm->GetResID(FramebufferRes(ctx, curDrawFBO)));
pipe.framebuffer.drawFBO.colorAttachments.resize(numCols);
for(GLint i = 0; i < numCols; i++)
{
ResourceId id =
rm->GetResID(rbCol[i] ? RenderbufferRes(ctx, curCol[i]) : TextureRes(ctx, curCol[i]));
pipe.framebuffer.drawFBO.colorAttachments[i].resource = rm->GetOriginalID(id);
GLenum attachment = GLenum(eGL_COLOR_ATTACHMENT0 + i);
if(pipe.framebuffer.drawFBO.colorAttachments[i].resource != ResourceId() && !rbCol[i])
GetFramebufferMipAndLayer(curDrawFBO, attachment,
(GLint *)&pipe.framebuffer.drawFBO.colorAttachments[i].firstMip,
(GLint *)&pipe.framebuffer.drawFBO.colorAttachments[i].firstSlice);
pipe.framebuffer.drawFBO.colorAttachments[i].numSlices = 1;
if(!rbCol[i] && id != ResourceId())
{
// desktop GL allows layered attachments which attach all slices from 0 to N
if(!IsGLES)
{
GLint layered = 0;
GL.glGetNamedFramebufferAttachmentParameterivEXT(
curDrawFBO, attachment, eGL_FRAMEBUFFER_ATTACHMENT_LAYERED, &layered);
if(layered)
{
pipe.framebuffer.drawFBO.colorAttachments[i].numSlices =
m_pDriver->m_Textures[id].depth & 0xffff;
}
}
else
{
// on GLES there's an OVR extension that allows attaching multiple layers
if(HasExt[OVR_multiview])
{
GLint numViews = 0, startView = 0;
GL.glGetNamedFramebufferAttachmentParameterivEXT(
curDrawFBO, attachment, eGL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR, &numViews);
GL.glGetNamedFramebufferAttachmentParameterivEXT(
curDrawFBO, attachment, eGL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR,
&startView);
if(numViews > 1)
{
pipe.framebuffer.drawFBO.colorAttachments[i].numSlices = numViews & 0xffff;
pipe.framebuffer.drawFBO.colorAttachments[i].firstSlice = startView & 0xffff;
}
}
}
}
GLenum swizzles[4] = {eGL_RED, eGL_GREEN, eGL_BLUE, eGL_ALPHA};
if(!rbCol[i] && id != ResourceId() &&
(HasExt[ARB_texture_swizzle] || HasExt[EXT_texture_swizzle]))
{
GLenum target = m_pDriver->m_Textures[id].curType;
GetTextureSwizzle(curCol[i], target, swizzles);
}
pipe.framebuffer.drawFBO.colorAttachments[i].swizzle.red = MakeSwizzle(swizzles[0]);
pipe.framebuffer.drawFBO.colorAttachments[i].swizzle.green = MakeSwizzle(swizzles[1]);
pipe.framebuffer.drawFBO.colorAttachments[i].swizzle.blue = MakeSwizzle(swizzles[2]);
pipe.framebuffer.drawFBO.colorAttachments[i].swizzle.alpha = MakeSwizzle(swizzles[3]);
}
ResourceId id =
rm->GetResID(rbDepth ? RenderbufferRes(ctx, curDepth) : TextureRes(ctx, curDepth));
pipe.framebuffer.drawFBO.depthAttachment.resource = rm->GetOriginalID(id);
pipe.framebuffer.drawFBO.stencilAttachment.resource = rm->GetOriginalID(
rm->GetResID(rbStencil ? RenderbufferRes(ctx, curStencil) : TextureRes(ctx, curStencil)));
if(pipe.framebuffer.drawFBO.depthAttachment.resource != ResourceId() && !rbDepth)
GetFramebufferMipAndLayer(curDrawFBO, eGL_DEPTH_ATTACHMENT,
&pipe.framebuffer.drawFBO.depthAttachment.firstMip,
&pipe.framebuffer.drawFBO.depthAttachment.firstSlice);
if(pipe.framebuffer.drawFBO.stencilAttachment.resource != ResourceId() && !rbStencil)
GetFramebufferMipAndLayer(curDrawFBO, eGL_STENCIL_ATTACHMENT,
&pipe.framebuffer.drawFBO.stencilAttachment.firstMip,
&pipe.framebuffer.drawFBO.stencilAttachment.firstSlice);
pipe.framebuffer.drawFBO.depthAttachment.numSlices = 1;
pipe.framebuffer.drawFBO.stencilAttachment.numSlices = 1;
if(!rbDepth && id != ResourceId())
{
// desktop GL allows layered attachments which attach all slices from 0 to N
if(!IsGLES)
{
GLint layered = 0;
GL.glGetNamedFramebufferAttachmentParameterivEXT(
curDrawFBO, eGL_DEPTH_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_LAYERED, &layered);
if(layered)
{
pipe.framebuffer.drawFBO.depthAttachment.numSlices =
m_pDriver->m_Textures[id].depth & 0xffff;
}
}
else
{
// on GLES there's an OVR extension that allows attaching multiple layers
if(HasExt[OVR_multiview])
{
GLint numViews = 0, startView = 0;
GL.glGetNamedFramebufferAttachmentParameterivEXT(
curDrawFBO, eGL_DEPTH_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR,
&numViews);
GL.glGetNamedFramebufferAttachmentParameterivEXT(
curDrawFBO, eGL_DEPTH_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR, &startView);
if(numViews > 1)
{
pipe.framebuffer.drawFBO.depthAttachment.numSlices = numViews & 0xffff;
pipe.framebuffer.drawFBO.depthAttachment.firstSlice = startView & 0xffff;
}
}
}
if(pipe.framebuffer.drawFBO.stencilAttachment.resource ==
pipe.framebuffer.drawFBO.depthAttachment.resource)
{
pipe.framebuffer.drawFBO.stencilAttachment.firstSlice =
pipe.framebuffer.drawFBO.depthAttachment.firstSlice;
pipe.framebuffer.drawFBO.stencilAttachment.numSlices =
pipe.framebuffer.drawFBO.depthAttachment.numSlices;
}
}
pipe.framebuffer.drawFBO.drawBuffers.resize(numCols);
for(GLint i = 0; i < numCols; i++)
{
GLenum b = eGL_NONE;
drv.glGetIntegerv(GLenum(eGL_DRAW_BUFFER0 + i), (GLint *)&b);
if(b >= eGL_COLOR_ATTACHMENT0 && b <= GLenum(eGL_COLOR_ATTACHMENT0 + numCols))
pipe.framebuffer.drawFBO.drawBuffers[i] = b - eGL_COLOR_ATTACHMENT0;
else
pipe.framebuffer.drawFBO.drawBuffers[i] = -1;
}
pipe.framebuffer.drawFBO.readBuffer = -1;
}
{
GLenum type = eGL_TEXTURE;
for(GLint i = 0; i < numCols; i++)
{
drv.glGetFramebufferAttachmentParameteriv(
eGL_READ_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + i),
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint *)&curCol[i]);
drv.glGetFramebufferAttachmentParameteriv(
eGL_READ_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + i),
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint *)&type);
if(type == eGL_RENDERBUFFER)
rbCol[i] = true;
}
drv.glGetFramebufferAttachmentParameteriv(eGL_READ_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
(GLint *)&curDepth);
drv.glGetFramebufferAttachmentParameteriv(eGL_READ_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint *)&type);
if(type == eGL_RENDERBUFFER)
rbDepth = true;
drv.glGetFramebufferAttachmentParameteriv(eGL_READ_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
(GLint *)&curStencil);
drv.glGetFramebufferAttachmentParameteriv(eGL_READ_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint *)&type);
if(type == eGL_RENDERBUFFER)
rbStencil = true;
pipe.framebuffer.readFBO.resourceId =
rm->GetOriginalID(rm->GetResID(FramebufferRes(ctx, curReadFBO)));
pipe.framebuffer.readFBO.colorAttachments.resize(numCols);
for(GLint i = 0; i < numCols; i++)
{
pipe.framebuffer.readFBO.colorAttachments[i].resource = rm->GetOriginalID(
rm->GetResID(rbCol[i] ? RenderbufferRes(ctx, curCol[i]) : TextureRes(ctx, curCol[i])));
if(pipe.framebuffer.readFBO.colorAttachments[i].resource != ResourceId() && !rbCol[i])
GetFramebufferMipAndLayer(curReadFBO, GLenum(eGL_COLOR_ATTACHMENT0 + i),
&pipe.framebuffer.readFBO.colorAttachments[i].firstMip,
&pipe.framebuffer.readFBO.colorAttachments[i].firstSlice);
}
pipe.framebuffer.readFBO.depthAttachment.resource = rm->GetOriginalID(
rm->GetResID(rbDepth ? RenderbufferRes(ctx, curDepth) : TextureRes(ctx, curDepth)));
pipe.framebuffer.readFBO.stencilAttachment.resource = rm->GetOriginalID(
rm->GetResID(rbStencil ? RenderbufferRes(ctx, curStencil) : TextureRes(ctx, curStencil)));
if(pipe.framebuffer.readFBO.depthAttachment.resource != ResourceId() && !rbDepth)
GetFramebufferMipAndLayer(curReadFBO, eGL_DEPTH_ATTACHMENT,
&pipe.framebuffer.readFBO.depthAttachment.firstMip,
&pipe.framebuffer.readFBO.depthAttachment.firstSlice);
if(pipe.framebuffer.readFBO.stencilAttachment.resource != ResourceId() && !rbStencil)
GetFramebufferMipAndLayer(curReadFBO, eGL_STENCIL_ATTACHMENT,
&pipe.framebuffer.readFBO.stencilAttachment.firstMip,
&pipe.framebuffer.readFBO.stencilAttachment.firstSlice);
pipe.framebuffer.readFBO.drawBuffers.resize(numCols);
for(GLint i = 0; i < numCols; i++)
pipe.framebuffer.readFBO.drawBuffers[i] = -1;
GLenum b = eGL_NONE;
drv.glGetIntegerv(eGL_READ_BUFFER, (GLint *)&b);
if(b >= eGL_COLOR_ATTACHMENT0 && b <= GLenum(eGL_COLOR_ATTACHMENT0 + numCols))
pipe.framebuffer.drawFBO.readBuffer = b - eGL_COLOR_ATTACHMENT0;
else
pipe.framebuffer.drawFBO.readBuffer = -1;
}
pipe.framebuffer.blendState.blendFactor = rs.BlendColor;
pipe.framebuffer.framebufferSRGB = rs.Enabled[GLRenderState::eEnabled_FramebufferSRGB];
pipe.framebuffer.dither = rs.Enabled[GLRenderState::eEnabled_Dither];
RDCCOMPILE_ASSERT(ARRAY_COUNT(rs.Blends) == ARRAY_COUNT(rs.ColorMasks),
"Color masks and blends mismatched");
pipe.framebuffer.blendState.blends.resize(ARRAY_COUNT(rs.Blends));
for(size_t i = 0; i < ARRAY_COUNT(rs.Blends); i++)
{
pipe.framebuffer.blendState.blends[i].enabled = rs.Blends[i].Enabled;
pipe.framebuffer.blendState.blends[i].logicOperation = LogicOperation::NoOp;
if(rs.LogicOp != eGL_NONE && rs.LogicOp != eGL_COPY)
pipe.framebuffer.blendState.blends[i].logicOperation = MakeLogicOp(rs.LogicOp);
pipe.framebuffer.blendState.blends[i].logicOperationEnabled =
rs.Enabled[GLRenderState::eEnabled_ColorLogicOp];
pipe.framebuffer.blendState.blends[i].colorBlend.source =
MakeBlendMultiplier(rs.Blends[i].SourceRGB);
pipe.framebuffer.blendState.blends[i].colorBlend.destination =
MakeBlendMultiplier(rs.Blends[i].DestinationRGB);
pipe.framebuffer.blendState.blends[i].colorBlend.operation =
MakeBlendOp(rs.Blends[i].EquationRGB);
pipe.framebuffer.blendState.blends[i].alphaBlend.source =
MakeBlendMultiplier(rs.Blends[i].SourceAlpha);
pipe.framebuffer.blendState.blends[i].alphaBlend.destination =
MakeBlendMultiplier(rs.Blends[i].DestinationAlpha);
pipe.framebuffer.blendState.blends[i].alphaBlend.operation =
MakeBlendOp(rs.Blends[i].EquationAlpha);
pipe.framebuffer.blendState.blends[i].writeMask = 0;
if(rs.ColorMasks[i].red)
pipe.framebuffer.blendState.blends[i].writeMask |= 1;
if(rs.ColorMasks[i].green)
pipe.framebuffer.blendState.blends[i].writeMask |= 2;
if(rs.ColorMasks[i].blue)
pipe.framebuffer.blendState.blends[i].writeMask |= 4;
if(rs.ColorMasks[i].alpha)
pipe.framebuffer.blendState.blends[i].writeMask |= 8;
}
switch(rs.Hints.Derivatives)
{
default:
case eGL_DONT_CARE: pipe.hints.derivatives = QualityHint::DontCare; break;
case eGL_NICEST: pipe.hints.derivatives = QualityHint::Nicest; break;
case eGL_FASTEST: pipe.hints.derivatives = QualityHint::Fastest; break;
}
switch(rs.Hints.LineSmooth)
{
default:
case eGL_DONT_CARE: pipe.hints.lineSmoothing = QualityHint::DontCare; break;
case eGL_NICEST: pipe.hints.lineSmoothing = QualityHint::Nicest; break;
case eGL_FASTEST: pipe.hints.lineSmoothing = QualityHint::Fastest; break;
}
switch(rs.Hints.PolySmooth)
{
default:
case eGL_DONT_CARE: pipe.hints.polySmoothing = QualityHint::DontCare; break;
case eGL_NICEST: pipe.hints.polySmoothing = QualityHint::Nicest; break;
case eGL_FASTEST: pipe.hints.polySmoothing = QualityHint::Fastest; break;
}
switch(rs.Hints.TexCompression)
{
default:
case eGL_DONT_CARE: pipe.hints.textureCompression = QualityHint::DontCare; break;
case eGL_NICEST: pipe.hints.textureCompression = QualityHint::Nicest; break;
case eGL_FASTEST: pipe.hints.textureCompression = QualityHint::Fastest; break;
}
pipe.hints.lineSmoothingEnabled = rs.Enabled[GLRenderState::eEnabled_LineSmooth];
pipe.hints.polySmoothingEnabled = rs.Enabled[GLRenderState::eEnabled_PolySmooth];
}