mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-07-08 16:50:44 +00:00
Add reporting of GL texture completeness and conflicts for descriptors
This commit is contained in:
@@ -450,6 +450,7 @@ TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, GLPipe, Sampler)
|
||||
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, GLPipe, Texture)
|
||||
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, GLPipe, VertexBuffer)
|
||||
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, GLPipe, VertexAttribute)
|
||||
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, GLPipe, TextureCompleteness)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// declare a function for passing external objects into python
|
||||
|
||||
@@ -323,6 +323,42 @@ multiple uniforms are pointing to the same binding but with different types. In
|
||||
impossible to disambiguate which binding was used.
|
||||
|
||||
|
||||
If this string is empty, no conflict is present. Otherwise it contains the bindings which are
|
||||
in conflict and their types.
|
||||
)");
|
||||
rdcstr typeConflict;
|
||||
};
|
||||
|
||||
DOCUMENT("Describes the a texture completeness issue of a descriptor.");
|
||||
struct TextureCompleteness
|
||||
{
|
||||
DOCUMENT("");
|
||||
TextureCompleteness() = default;
|
||||
TextureCompleteness(const TextureCompleteness &) = default;
|
||||
TextureCompleteness &operator=(const TextureCompleteness &) = default;
|
||||
|
||||
bool operator==(const TextureCompleteness &o) const
|
||||
{
|
||||
return descriptorByteOffset == o.descriptorByteOffset && completeStatus == o.completeStatus;
|
||||
}
|
||||
bool operator<(const TextureCompleteness &o) const
|
||||
{
|
||||
return descriptorByteOffset < o.descriptorByteOffset;
|
||||
}
|
||||
|
||||
DOCUMENT(R"(The byte offset in the GL descriptor storage of the problematic descriptor
|
||||
)");
|
||||
uint64_t descriptorByteOffset = 0;
|
||||
|
||||
DOCUMENT(R"(The details of the texture's (in)completeness. If this string is empty, the texture is
|
||||
complete. Otherwise it contains an explanation of why the texture is believed to be incomplete.
|
||||
)");
|
||||
rdcstr completeStatus;
|
||||
|
||||
DOCUMENT(R"(The details of any type conflict on this binding. This can happen if
|
||||
multiple uniforms are pointing to the same binding but with different types. In this case it is
|
||||
impossible to disambiguate which binding was used.
|
||||
|
||||
If this string is empty, no conflict is present. Otherwise it contains the bindings which are
|
||||
in conflict and their types.
|
||||
)");
|
||||
@@ -931,6 +967,12 @@ struct State
|
||||
)");
|
||||
uint32_t descriptorByteSize;
|
||||
|
||||
DOCUMENT(R"(Texture completeness issues of descriptors in the descriptor store.
|
||||
|
||||
:type: GLTextureCompleteness
|
||||
)");
|
||||
rdcarray<TextureCompleteness> textureCompleteness;
|
||||
|
||||
DOCUMENT(R"(The transform feedback stage.
|
||||
|
||||
:type: GLFeedback
|
||||
@@ -977,6 +1019,7 @@ DECLARE_REFLECTION_STRUCT(GLPipe::Shader);
|
||||
DECLARE_REFLECTION_STRUCT(GLPipe::FixedVertexProcessing);
|
||||
DECLARE_REFLECTION_STRUCT(GLPipe::Texture);
|
||||
DECLARE_REFLECTION_STRUCT(GLPipe::Sampler);
|
||||
DECLARE_REFLECTION_STRUCT(GLPipe::TextureCompleteness);
|
||||
DECLARE_REFLECTION_STRUCT(GLPipe::Buffer);
|
||||
DECLARE_REFLECTION_STRUCT(GLPipe::ImageLoadStore);
|
||||
DECLARE_REFLECTION_STRUCT(GLPipe::Feedback);
|
||||
|
||||
@@ -1193,6 +1193,8 @@ void GLReplay::SavePipelineState(uint32_t eventId)
|
||||
ResortBindings(refls[i], mappings[i]);
|
||||
}
|
||||
|
||||
pipe.textureCompleteness.clear();
|
||||
|
||||
for(size_t s = 0; s < NumShaderStages; s++)
|
||||
{
|
||||
if(!refls[s])
|
||||
@@ -1258,6 +1260,62 @@ void GLReplay::SavePipelineState(uint32_t eventId)
|
||||
access.byteOffset =
|
||||
EncodeGLDescriptorIndex({descType, (uint32_t)mapping.readOnlyResources[i].bind});
|
||||
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 + mapping.readOnlyResources[i].bind));
|
||||
|
||||
GLenum binding = eGL_NONE;
|
||||
switch(refls[s]->readOnlyResources[i].resType)
|
||||
{
|
||||
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(mapping.readWriteResources.size() < 0xffff, mapping.readWriteResources.size());
|
||||
@@ -1293,6 +1351,120 @@ void GLReplay::SavePipelineState(uint32_t eventId)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// search for conflicts by looking at all stages
|
||||
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.resType)
|
||||
{
|
||||
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.resType;
|
||||
}
|
||||
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.resType).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])
|
||||
|
||||
@@ -1842,6 +1842,16 @@ void DoSerialise(SerialiserType &ser, GLPipe::Texture &el)
|
||||
SIZE_CHECK(80);
|
||||
}
|
||||
|
||||
template <typename SerialiserType>
|
||||
void DoSerialise(SerialiserType &ser, GLPipe::TextureCompleteness &el)
|
||||
{
|
||||
SERIALISE_MEMBER(descriptorByteOffset);
|
||||
SERIALISE_MEMBER(completeStatus);
|
||||
SERIALISE_MEMBER(typeConflict);
|
||||
|
||||
SIZE_CHECK(56);
|
||||
}
|
||||
|
||||
template <typename SerialiserType>
|
||||
void DoSerialise(SerialiserType &ser, GLPipe::Sampler &el)
|
||||
{
|
||||
@@ -2048,6 +2058,7 @@ void DoSerialise(SerialiserType &ser, GLPipe::State &el)
|
||||
SERIALISE_MEMBER(descriptorStore);
|
||||
SERIALISE_MEMBER(descriptorCount);
|
||||
SERIALISE_MEMBER(descriptorByteSize);
|
||||
SERIALISE_MEMBER(textureCompleteness);
|
||||
|
||||
SERIALISE_MEMBER(transformFeedback);
|
||||
|
||||
@@ -2059,7 +2070,7 @@ void DoSerialise(SerialiserType &ser, GLPipe::State &el)
|
||||
|
||||
SERIALISE_MEMBER(hints);
|
||||
|
||||
SIZE_CHECK(1968);
|
||||
SIZE_CHECK(1992);
|
||||
}
|
||||
|
||||
#pragma endregion OpenGL pipeline state
|
||||
|
||||
Reference in New Issue
Block a user