Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 64 additions & 8 deletions impeller/renderer/backend/gles/proc_table_gles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,11 @@ ProcTableGLES::ProcTableGLES( // NOLINT(google-readability-function-size)

resolver = WrappedResolver(resolver);

auto error_fn = reinterpret_cast<PFNGLGETERRORPROC>(resolver("glGetError"));
if (!error_fn) {
VALIDATION_LOG << "Could not resolve " << "glGetError";
return;
}

#define IMPELLER_PROC(proc_ivar) \
if (auto fn_ptr = resolver(proc_ivar.name)) { \
proc_ivar.function = \
reinterpret_cast<decltype(proc_ivar.function)>(fn_ptr); \
proc_ivar.error_fn = error_fn; \
RegisterProc(&proc_ivar); \
} else { \
VALIDATION_LOG << "Could not resolve " << proc_ivar.name; \
return; \
Expand All @@ -119,7 +113,7 @@ ProcTableGLES::ProcTableGLES( // NOLINT(google-readability-function-size)
if (auto fn_ptr = resolver(proc_ivar.name)) { \
proc_ivar.function = \
reinterpret_cast<decltype(proc_ivar.function)>(fn_ptr); \
proc_ivar.error_fn = error_fn; \
RegisterProc(&proc_ivar); \
}
FOR_EACH_IMPELLER_GLES3_PROC(IMPELLER_PROC);
FOR_EACH_IMPELLER_EXT_PROC(IMPELLER_PROC);
Expand All @@ -144,6 +138,10 @@ ProcTableGLES::ProcTableGLES( // NOLINT(google-readability-function-size)
// builds to identify threading violations in the engine.
UseProgram.enforce_one_thread = true;

#ifndef NDEBUG
SetDebugGLErrorChecking(true);
#endif // NDEBUG

is_valid_ = true;
}

Expand Down Expand Up @@ -431,4 +429,62 @@ std::string ProcTableGLES::GetProgramInfoLogString(GLuint program) const {
static_cast<size_t>(length)};
}

void ProcTableGLES::RegisterProc(GLProcBase* proc) {
if (proc && proc->name) {
debug_known_procs_[proc->name] = proc;
}
}

void ProcTableGLES::IterateDebugProcs(
const std::function<bool(GLProcBase*)>& iterator) const {
if (!iterator) {
return;
}
for (const auto& proc : debug_known_procs_) {
if (!iterator(proc.second)) {
return;
}
}
}

void ProcTableGLES::SetDebugGLCallLogging(bool log) const {
IterateDebugProcs([log](GLProcBase* proc) -> bool {
proc->log_calls = log;
return true;
});
}

void ProcTableGLES::SetDebugGLCallLogging(bool log,
const char* function_name) const {
IterateDebugProcs([log, function_name](GLProcBase* proc) -> bool {
if (proc->name == function_name) {
proc->log_calls = log;
return false;
}
return true;
});
}

void ProcTableGLES::SetDebugGLErrorChecking(bool check) const {
FML_DCHECK(GetError.function != nullptr);
PFNGLGETERRORPROC error_fn = check ? GetError.function : nullptr;
IterateDebugProcs([error_fn](GLProcBase* proc) -> bool {
proc->error_fn = error_fn;
return true;
});
}

void ProcTableGLES::SetDebugGLErrorChecking(bool check,
const char* function_name) const {
FML_DCHECK(GetError.function != nullptr);
PFNGLGETERRORPROC error_fn = check ? GetError.function : nullptr;
IterateDebugProcs([error_fn, function_name](GLProcBase* proc) -> bool {
if (proc->name == function_name) {
proc->error_fn = error_fn;
return false;
}
return true;
});
}

} // namespace impeller
93 changes: 78 additions & 15 deletions impeller/renderer/backend/gles/proc_table_gles.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#define FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_PROC_TABLE_GLES_H_

#include <functional>
#include <map>
#include <mutex>
#include <string>
#include <thread>
Expand Down Expand Up @@ -72,21 +73,13 @@ template <class... Type>
return stream.str();
}

template <class T>
struct GLProc {
using GLFunctionType = T;

struct GLProcBase {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this get split out?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The map that enable "lookup proc by name" needs an un-templated field.

//----------------------------------------------------------------------------
/// The name of the GL function.
///
// TODO(135922) Change to string_view.
const char* name = nullptr;

//----------------------------------------------------------------------------
/// The pointer to the GL function.
///
GLFunctionType* function = nullptr;

//----------------------------------------------------------------------------
/// An optional error function. If present, all calls will be followed by an
/// error check.
Expand All @@ -109,6 +102,20 @@ struct GLProc {
/// thread.
bool enforce_one_thread = false;

explicit GLProcBase(const char* p_name) : name(p_name) {}
};

template <class T>
struct GLProc : public GLProcBase {
using GLFunctionType = T;

explicit GLProc(const char* p_name) : GLProcBase(p_name) {}

//----------------------------------------------------------------------------
/// The pointer to the GL function.
///
GLFunctionType* function = nullptr;

//----------------------------------------------------------------------------
/// @brief Call the GL function with the appropriate parameters. Lookup
/// the documentation for the GL function being called to
Expand All @@ -117,7 +124,7 @@ struct GLProc {
///
template <class... Args>
auto operator()(Args&&... args) const {
#if defined(IMPELLER_DEBUG) && !defined(NDEBUG)
#if defined(IMPELLER_DEBUG)
AutoErrorCheck error(error_fn, name);
// We check for the existence of extensions, and reset the function pointer
// but it's still called unconditionally below, and will segfault. This
Expand All @@ -138,7 +145,7 @@ struct GLProc {
"thread. As of this addition, the design of the engine should be "
"using non-raster threads only for uploading images.";
}
#endif // defined(IMPELLER_DEBUG) && !defined(NDEBUG)
#endif // defined(IMPELLER_DEBUG)
return function(std::forward<Args>(args)...);
}

Expand Down Expand Up @@ -201,13 +208,15 @@ struct GLProc {
PROC(GenVertexArrays); \
PROC(GetActiveUniform); \
PROC(GetBooleanv); \
PROC(GetError); \
PROC(GetFloatv); \
PROC(GetFramebufferAttachmentParameteriv); \
PROC(GetIntegerv); \
PROC(GetProgramInfoLog); \
PROC(GetProgramiv); \
PROC(GetShaderInfoLog); \
PROC(GetShaderiv); \
PROC(GetShaderSource); \
PROC(GetString); \
PROC(GetStringi); \
PROC(GetUniformLocation); \
Expand All @@ -227,9 +236,9 @@ struct GLProc {
PROC(StencilMaskSeparate); \
PROC(StencilOpSeparate); \
PROC(TexImage2D); \
PROC(TexSubImage2D); \
PROC(TexParameteri); \
PROC(TexParameterfv); \
PROC(TexParameteri); \
PROC(TexSubImage2D); \
PROC(Uniform1fv); \
PROC(Uniform1i); \
PROC(Uniform2fv); \
Expand All @@ -239,7 +248,6 @@ struct GLProc {
PROC(UseProgram); \
PROC(VertexAttribPointer); \
PROC(Viewport); \
PROC(GetShaderSource); \
PROC(ReadPixels);

// Calls specific to OpenGLES.
Expand Down Expand Up @@ -293,7 +301,7 @@ class ProcTableGLES {
~ProcTableGLES();

#define IMPELLER_PROC(name) \
GLProc<decltype(gl##name)> name = {"gl" #name, nullptr};
GLProc<decltype(gl##name)> name = GLProc<decltype(gl##name)>{"gl" #name};

FOR_EACH_IMPELLER_PROC(IMPELLER_PROC);
FOR_EACH_IMPELLER_ES_ONLY_PROC(IMPELLER_PROC);
Expand Down Expand Up @@ -332,6 +340,55 @@ class ProcTableGLES {

void PopDebugGroup() const;

//----------------------------------------------------------------------------
/// @brief Set if all OpenGL function calls in this proc table log their
/// invocation and arguments.
///
/// Example:
/// ```
/// glDepthMask(1)
/// glViewport(0, 0, 2048, 1536)
/// glDepthRangef(0, 1)
/// glDisable(2884)
/// glFrontFace(2304)
/// ```
///
/// @warning Call logging is only available in IMPELLER_DEBUG builds.
///
/// @param[in] log If logging should be enabled.
///
void SetDebugGLCallLogging(bool log) const;

//----------------------------------------------------------------------------
/// @brief Set if the a specific OpenGL function call logs its invocation
/// and arguments.
///
/// @warning Call logging is only available in IMPELLER_DEBUG builds.
///
/// @param[in] log If logging should be enabled.
///
void SetDebugGLCallLogging(bool log, const char* function_name) const;

//----------------------------------------------------------------------------
/// @brief Set if glGetError is called and trapped on all OpenGL function
/// calls in this proc table.
///
/// @warning GL error checking is only available in IMPELLER_DEBUG builds.
///
/// @param[in] check If error checking should be performed.
///
void SetDebugGLErrorChecking(bool check) const;

//----------------------------------------------------------------------------
/// @brief Set if glGetError is called and trapped on a specific OpenGL
/// function in this proc table.
///
/// @warning GL error checking is only available in IMPELLER_DEBUG builds.
///
/// @param[in] check If error checking should be performed.
///
void SetDebugGLErrorChecking(bool check, const char* function_name) const;

// Visible For testing.
std::optional<std::string> ComputeShaderWithDefines(
const fml::Mapping& mapping,
Expand All @@ -342,10 +399,16 @@ class ProcTableGLES {
std::unique_ptr<DescriptionGLES> description_;
std::shared_ptr<const CapabilitiesGLES> capabilities_;
GLint debug_label_max_length_ = 0;
std::map<std::string, GLProcBase*> debug_known_procs_;

ProcTableGLES(const ProcTableGLES&) = delete;

ProcTableGLES& operator=(const ProcTableGLES&) = delete;

void IterateDebugProcs(
const std::function<bool(GLProcBase*)>& iterator) const;

void RegisterProc(GLProcBase* proc);
};

} // namespace impeller
Expand Down
34 changes: 34 additions & 0 deletions impeller/renderer/backend/gles/test/proc_table_gles_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,39 @@ TEST(ProcTableGLES, ResolvesCorrectClearDepthProcOnDesktopGL) {
FOR_EACH_IMPELLER_ES_ONLY_PROC(EXPECT_UNAVAILABLE);
}

TEST(ProcTableGLES, DebuggingOptionsAreRespected) {
#ifndef IMPELLER_DEBUG
if ((true)) {
GTEST_SKIP() << "Debugging is only available in IMPELLER_DEBUG";
}
#endif // IMPELLER_DEBUG
auto mock_gles = MockGLES::Init(std::nullopt, "OpenGL ES 3.0");
auto& gl = mock_gles->GetProcTable();
// Check call logging.
{
gl.SetDebugGLCallLogging(false);
ASSERT_FALSE(gl.Clear.log_calls);
gl.SetDebugGLCallLogging(true);
ASSERT_TRUE(gl.Clear.log_calls);
gl.SetDebugGLCallLogging(false);
ASSERT_FALSE(gl.Clear.log_calls);
gl.SetDebugGLCallLogging(true, "glClear");
ASSERT_TRUE(gl.Clear.log_calls);
gl.SetDebugGLCallLogging(false, "glClear");
ASSERT_FALSE(gl.Clear.log_calls);
}
// Check error checking.
{
gl.SetDebugGLErrorChecking(true);
ASSERT_NE(gl.Clear.error_fn, nullptr);
gl.SetDebugGLErrorChecking(false);
ASSERT_EQ(gl.Clear.error_fn, nullptr);
gl.SetDebugGLErrorChecking(true, "glClear");
ASSERT_NE(gl.Clear.error_fn, nullptr);
gl.SetDebugGLErrorChecking(false, "glClear");
ASSERT_EQ(gl.Clear.error_fn, nullptr);
}
}

} // namespace testing
} // namespace impeller
3 changes: 2 additions & 1 deletion impeller/toolkit/glvk/proc_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ class ProcTable {
///
bool IsValid() const;

#define GLVK_PROC(name) GLProc<decltype(gl##name)> name = {"gl" #name, nullptr};
#define GLVK_PROC(name) \
GLProc<decltype(gl##name)> name = GLProc<decltype(gl##name)>("gl" #name);

FOR_EACH_GLVK_PROC(GLVK_PROC);

Expand Down
40 changes: 40 additions & 0 deletions impeller/toolkit/interop/context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,44 @@ AiksContext& Context::GetAiksContext() {
return context_;
}

void Context::SetDebugGLCallLogging(const char* function_name_or_null,
bool enable) {
#if IMPELLER_ENABLE_OPENGLES
if (context_.GetContext()->GetBackendType() !=
impeller::Context::BackendType::kOpenGLES) {
VALIDATION_LOG << "Not an OpenGL context.";
return;
}
auto& gl =
ContextGLES::Cast(*context_.GetContext()).GetReactor()->GetProcTable();
if (function_name_or_null == nullptr) {
gl.SetDebugGLCallLogging(enable);
} else {
gl.SetDebugGLCallLogging(enable, function_name_or_null);
}
#else // IMPELLER_ENABLE_OPENGLES
VALIDATION_LOG << "OpenGL unavailable."
#endif // IMPELLER_ENABLE_OPENGLES
}

void Context::SetDebugGLErrorChecking(const char* function_name_or_null,
bool enable) {
#if IMPELLER_ENABLE_OPENGLES
if (context_.GetContext()->GetBackendType() !=
impeller::Context::BackendType::kOpenGLES) {
VALIDATION_LOG << "Not an OpenGL context.";
return;
}
auto& gl =
ContextGLES::Cast(*context_.GetContext()).GetReactor()->GetProcTable();
if (function_name_or_null == nullptr) {
gl.SetDebugGLErrorChecking(enable);
} else {
gl.SetDebugGLErrorChecking(enable, function_name_or_null);
}
#else // IMPELLER_ENABLE_OPENGLES
VALIDATION_LOG << "OpenGL unavailable."
#endif // IMPELLER_ENABLE_OPENGLES
}

} // namespace impeller::interop
4 changes: 4 additions & 0 deletions impeller/toolkit/interop/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ class Context final

AiksContext& GetAiksContext();

void SetDebugGLCallLogging(const char* function_name_or_null, bool enable);

void SetDebugGLErrorChecking(const char* function_name_or_null, bool enable);

private:
impeller::AiksContext context_;
std::shared_ptr<BackendData> backend_data_;
Expand Down
Loading