Route shader runtime calls through renderer GL
This commit is contained in:
@@ -883,6 +883,153 @@ pp::foundation::Status bind_opengl_sampler_object(
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status use_opengl_program(
|
||||
std::uint32_t program_id,
|
||||
OpenGlProgramUseDispatch dispatch) noexcept
|
||||
{
|
||||
if (dispatch.use_program == nullptr) {
|
||||
return pp::foundation::Status::invalid_argument("OpenGL program use dispatch callback must not be null");
|
||||
}
|
||||
|
||||
dispatch.use_program(program_id);
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status delete_opengl_program(
|
||||
std::uint32_t program_id,
|
||||
OpenGlProgramDeleteDispatch dispatch) noexcept
|
||||
{
|
||||
if (dispatch.use_program == nullptr || dispatch.delete_program == nullptr) {
|
||||
return pp::foundation::Status::invalid_argument("OpenGL program delete dispatch callbacks must not be null");
|
||||
}
|
||||
|
||||
if (program_id == 0U) {
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
dispatch.use_program(default_framebuffer_id());
|
||||
dispatch.delete_program(program_id);
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status set_opengl_uniform_vec4(
|
||||
std::int32_t location,
|
||||
const float* values,
|
||||
OpenGlUniformVec4Dispatch dispatch) noexcept
|
||||
{
|
||||
if (dispatch.uniform_4fv == nullptr) {
|
||||
return pp::foundation::Status::invalid_argument("OpenGL vec4 uniform dispatch callback must not be null");
|
||||
}
|
||||
|
||||
if (values == nullptr) {
|
||||
return pp::foundation::Status::invalid_argument("OpenGL vec4 uniform values must not be null");
|
||||
}
|
||||
|
||||
dispatch.uniform_4fv(location, 1, values);
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status set_opengl_uniform_vec3(
|
||||
std::int32_t location,
|
||||
const float* values,
|
||||
OpenGlUniformVec3Dispatch dispatch) noexcept
|
||||
{
|
||||
if (dispatch.uniform_3fv == nullptr) {
|
||||
return pp::foundation::Status::invalid_argument("OpenGL vec3 uniform dispatch callback must not be null");
|
||||
}
|
||||
|
||||
if (values == nullptr) {
|
||||
return pp::foundation::Status::invalid_argument("OpenGL vec3 uniform values must not be null");
|
||||
}
|
||||
|
||||
dispatch.uniform_3fv(location, 1, values);
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status set_opengl_uniform_vec2(
|
||||
std::int32_t location,
|
||||
const float* values,
|
||||
OpenGlUniformVec2Dispatch dispatch) noexcept
|
||||
{
|
||||
if (dispatch.uniform_2fv == nullptr) {
|
||||
return pp::foundation::Status::invalid_argument("OpenGL vec2 uniform dispatch callback must not be null");
|
||||
}
|
||||
|
||||
if (values == nullptr) {
|
||||
return pp::foundation::Status::invalid_argument("OpenGL vec2 uniform values must not be null");
|
||||
}
|
||||
|
||||
dispatch.uniform_2fv(location, 1, values);
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status set_opengl_uniform_mat4(
|
||||
std::int32_t location,
|
||||
const float* values,
|
||||
OpenGlUniformMat4Dispatch dispatch) noexcept
|
||||
{
|
||||
if (dispatch.uniform_matrix_4fv == nullptr) {
|
||||
return pp::foundation::Status::invalid_argument("OpenGL mat4 uniform dispatch callback must not be null");
|
||||
}
|
||||
|
||||
if (values == nullptr) {
|
||||
return pp::foundation::Status::invalid_argument("OpenGL mat4 uniform values must not be null");
|
||||
}
|
||||
|
||||
dispatch.uniform_matrix_4fv(
|
||||
location,
|
||||
1,
|
||||
static_cast<std::uint8_t>(matrix_uniform_not_transposed()),
|
||||
values);
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status set_opengl_uniform_int(
|
||||
std::int32_t location,
|
||||
std::int32_t value,
|
||||
OpenGlUniformIntDispatch dispatch) noexcept
|
||||
{
|
||||
if (dispatch.uniform_1i == nullptr) {
|
||||
return pp::foundation::Status::invalid_argument("OpenGL int uniform dispatch callback must not be null");
|
||||
}
|
||||
|
||||
dispatch.uniform_1i(location, value);
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status set_opengl_uniform_float(
|
||||
std::int32_t location,
|
||||
float value,
|
||||
OpenGlUniformFloatDispatch dispatch) noexcept
|
||||
{
|
||||
if (dispatch.uniform_1f == nullptr) {
|
||||
return pp::foundation::Status::invalid_argument("OpenGL float uniform dispatch callback must not be null");
|
||||
}
|
||||
|
||||
dispatch.uniform_1f(location, value);
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Result<std::int32_t> get_opengl_attribute_location(
|
||||
std::uint32_t program_id,
|
||||
const char* attribute_name,
|
||||
OpenGlAttributeLocationDispatch dispatch) noexcept
|
||||
{
|
||||
if (dispatch.get_attrib_location == nullptr) {
|
||||
return pp::foundation::Result<std::int32_t>::failure(
|
||||
pp::foundation::Status::invalid_argument(
|
||||
"OpenGL attribute-location dispatch callback must not be null"));
|
||||
}
|
||||
|
||||
if (program_id == 0U || attribute_name == nullptr || attribute_name[0] == '\0') {
|
||||
return pp::foundation::Result<std::int32_t>::failure(
|
||||
pp::foundation::Status::invalid_argument("OpenGL attribute-location parameters are invalid"));
|
||||
}
|
||||
|
||||
return pp::foundation::Result<std::int32_t>::success(
|
||||
dispatch.get_attrib_location(program_id, attribute_name));
|
||||
}
|
||||
|
||||
std::uint32_t extension_count_query() noexcept
|
||||
{
|
||||
return gl_num_extensions;
|
||||
|
||||
@@ -220,6 +220,18 @@ using OpenGlScissorFn = void (*)(std::int32_t x, std::int32_t y, std::int32_t wi
|
||||
using OpenGlBlendFuncFn = void (*)(std::uint32_t source_factor, std::uint32_t destination_factor) noexcept;
|
||||
using OpenGlBlendEquationSeparateFn = void (*)(std::uint32_t color_equation, std::uint32_t alpha_equation) noexcept;
|
||||
using OpenGlUseProgramFn = void (*)(std::uint32_t program) noexcept;
|
||||
using OpenGlDeleteProgramFn = void (*)(std::uint32_t program) noexcept;
|
||||
using OpenGlUniform4fvFn = void (*)(std::int32_t location, std::int32_t count, const float* values) noexcept;
|
||||
using OpenGlUniform3fvFn = void (*)(std::int32_t location, std::int32_t count, const float* values) noexcept;
|
||||
using OpenGlUniform2fvFn = void (*)(std::int32_t location, std::int32_t count, const float* values) noexcept;
|
||||
using OpenGlUniformMatrix4fvFn = void (*)(
|
||||
std::int32_t location,
|
||||
std::int32_t count,
|
||||
std::uint8_t transpose,
|
||||
const float* values) noexcept;
|
||||
using OpenGlUniform1iFn = void (*)(std::int32_t location, std::int32_t value) noexcept;
|
||||
using OpenGlUniform1fFn = void (*)(std::int32_t location, float value) noexcept;
|
||||
using OpenGlGetAttribLocationFn = std::int32_t (*)(std::uint32_t program, const char* name) noexcept;
|
||||
using OpenGlBindFramebufferFn = void (*)(std::uint32_t target, std::uint32_t framebuffer) noexcept;
|
||||
using OpenGlBindTextureFn = void (*)(std::uint32_t target, std::uint32_t texture) noexcept;
|
||||
using OpenGlBindSamplerFn = void (*)(std::uint32_t unit, std::uint32_t sampler) noexcept;
|
||||
@@ -433,6 +445,43 @@ struct OpenGlSamplerBindDispatch {
|
||||
OpenGlBindSamplerFn bind_sampler = nullptr;
|
||||
};
|
||||
|
||||
struct OpenGlProgramUseDispatch {
|
||||
OpenGlUseProgramFn use_program = nullptr;
|
||||
};
|
||||
|
||||
struct OpenGlProgramDeleteDispatch {
|
||||
OpenGlUseProgramFn use_program = nullptr;
|
||||
OpenGlDeleteProgramFn delete_program = nullptr;
|
||||
};
|
||||
|
||||
struct OpenGlUniformVec4Dispatch {
|
||||
OpenGlUniform4fvFn uniform_4fv = nullptr;
|
||||
};
|
||||
|
||||
struct OpenGlUniformVec3Dispatch {
|
||||
OpenGlUniform3fvFn uniform_3fv = nullptr;
|
||||
};
|
||||
|
||||
struct OpenGlUniformVec2Dispatch {
|
||||
OpenGlUniform2fvFn uniform_2fv = nullptr;
|
||||
};
|
||||
|
||||
struct OpenGlUniformMat4Dispatch {
|
||||
OpenGlUniformMatrix4fvFn uniform_matrix_4fv = nullptr;
|
||||
};
|
||||
|
||||
struct OpenGlUniformIntDispatch {
|
||||
OpenGlUniform1iFn uniform_1i = nullptr;
|
||||
};
|
||||
|
||||
struct OpenGlUniformFloatDispatch {
|
||||
OpenGlUniform1fFn uniform_1f = nullptr;
|
||||
};
|
||||
|
||||
struct OpenGlAttributeLocationDispatch {
|
||||
OpenGlGetAttribLocationFn get_attrib_location = nullptr;
|
||||
};
|
||||
|
||||
[[nodiscard]] OpenGlCapabilities detect_opengl_capabilities(
|
||||
std::span<const std::string_view> extensions,
|
||||
OpenGlRuntime runtime) noexcept;
|
||||
@@ -520,6 +569,40 @@ struct OpenGlSamplerBindDispatch {
|
||||
std::uint32_t unit,
|
||||
std::uint32_t sampler_id,
|
||||
OpenGlSamplerBindDispatch dispatch) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status use_opengl_program(
|
||||
std::uint32_t program_id,
|
||||
OpenGlProgramUseDispatch dispatch) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status delete_opengl_program(
|
||||
std::uint32_t program_id,
|
||||
OpenGlProgramDeleteDispatch dispatch) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status set_opengl_uniform_vec4(
|
||||
std::int32_t location,
|
||||
const float* values,
|
||||
OpenGlUniformVec4Dispatch dispatch) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status set_opengl_uniform_vec3(
|
||||
std::int32_t location,
|
||||
const float* values,
|
||||
OpenGlUniformVec3Dispatch dispatch) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status set_opengl_uniform_vec2(
|
||||
std::int32_t location,
|
||||
const float* values,
|
||||
OpenGlUniformVec2Dispatch dispatch) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status set_opengl_uniform_mat4(
|
||||
std::int32_t location,
|
||||
const float* values,
|
||||
OpenGlUniformMat4Dispatch dispatch) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status set_opengl_uniform_int(
|
||||
std::int32_t location,
|
||||
std::int32_t value,
|
||||
OpenGlUniformIntDispatch dispatch) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status set_opengl_uniform_float(
|
||||
std::int32_t location,
|
||||
float value,
|
||||
OpenGlUniformFloatDispatch dispatch) noexcept;
|
||||
[[nodiscard]] pp::foundation::Result<std::int32_t> get_opengl_attribute_location(
|
||||
std::uint32_t program_id,
|
||||
const char* attribute_name,
|
||||
OpenGlAttributeLocationDispatch dispatch) noexcept;
|
||||
|
||||
[[nodiscard]] std::uint32_t extension_count_query() noexcept;
|
||||
[[nodiscard]] std::uint32_t extension_string_name() noexcept;
|
||||
|
||||
178
src/shader.cpp
178
src/shader.cpp
@@ -35,9 +35,57 @@ namespace {
|
||||
return static_cast<GLenum>(pp::renderer::gl::active_uniform_count_query());
|
||||
}
|
||||
|
||||
[[nodiscard]] GLboolean matrix_uniform_not_transposed() noexcept
|
||||
void use_opengl_program(std::uint32_t program) noexcept
|
||||
{
|
||||
return static_cast<GLboolean>(pp::renderer::gl::matrix_uniform_not_transposed());
|
||||
glUseProgram(static_cast<GLuint>(program));
|
||||
}
|
||||
|
||||
void delete_opengl_program(std::uint32_t program) noexcept
|
||||
{
|
||||
glDeleteProgram(static_cast<GLuint>(program));
|
||||
}
|
||||
|
||||
void set_opengl_uniform_4fv(std::int32_t location, std::int32_t count, const float* values) noexcept
|
||||
{
|
||||
glUniform4fv(static_cast<GLint>(location), static_cast<GLsizei>(count), values);
|
||||
}
|
||||
|
||||
void set_opengl_uniform_3fv(std::int32_t location, std::int32_t count, const float* values) noexcept
|
||||
{
|
||||
glUniform3fv(static_cast<GLint>(location), static_cast<GLsizei>(count), values);
|
||||
}
|
||||
|
||||
void set_opengl_uniform_2fv(std::int32_t location, std::int32_t count, const float* values) noexcept
|
||||
{
|
||||
glUniform2fv(static_cast<GLint>(location), static_cast<GLsizei>(count), values);
|
||||
}
|
||||
|
||||
void set_opengl_uniform_matrix_4fv(
|
||||
std::int32_t location,
|
||||
std::int32_t count,
|
||||
std::uint8_t transpose,
|
||||
const float* values) noexcept
|
||||
{
|
||||
glUniformMatrix4fv(
|
||||
static_cast<GLint>(location),
|
||||
static_cast<GLsizei>(count),
|
||||
static_cast<GLboolean>(transpose),
|
||||
values);
|
||||
}
|
||||
|
||||
void set_opengl_uniform_1i(std::int32_t location, std::int32_t value) noexcept
|
||||
{
|
||||
glUniform1i(static_cast<GLint>(location), static_cast<GLint>(value));
|
||||
}
|
||||
|
||||
void set_opengl_uniform_1f(std::int32_t location, float value) noexcept
|
||||
{
|
||||
glUniform1f(static_cast<GLint>(location), value);
|
||||
}
|
||||
|
||||
std::int32_t get_opengl_attrib_location(std::uint32_t program, const char* name) noexcept
|
||||
{
|
||||
return static_cast<std::int32_t>(glGetAttribLocation(static_cast<GLuint>(program), name));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -65,14 +113,12 @@ std::string Shader::read(const std::string& path)
|
||||
std::string data((char*)a.read_all(), a.m_len);
|
||||
|
||||
// split path
|
||||
std::string name, base, ext;
|
||||
std::string base;
|
||||
std::regex reg_path(R"((.*)[\\/]([^\\/]+)\.(\w+)$)");
|
||||
std::smatch m;
|
||||
if (std::regex_search(path, m, reg_path))
|
||||
{
|
||||
base = m[1].str();
|
||||
name = m[2].str();
|
||||
ext = m[3].str();
|
||||
}
|
||||
|
||||
for (const auto& l : split(data, '\n'))
|
||||
@@ -319,8 +365,14 @@ void Shader::destroy()
|
||||
{
|
||||
App::I->render_task_async([prog=prog]
|
||||
{
|
||||
glUseProgram(0);
|
||||
glDeleteProgram(prog);
|
||||
const auto status = pp::renderer::gl::delete_opengl_program(
|
||||
static_cast<std::uint32_t>(prog),
|
||||
pp::renderer::gl::OpenGlProgramDeleteDispatch {
|
||||
.use_program = use_opengl_program,
|
||||
.delete_program = delete_opengl_program,
|
||||
});
|
||||
if (!status.ok())
|
||||
LOG("Shader::destroy() failed because: %s", status.message);
|
||||
});
|
||||
prog = 0;
|
||||
}
|
||||
@@ -329,54 +381,148 @@ void Shader::destroy()
|
||||
|
||||
void Shader::use()
|
||||
{
|
||||
glUseProgram(prog);
|
||||
const auto status = pp::renderer::gl::use_opengl_program(
|
||||
static_cast<std::uint32_t>(prog),
|
||||
pp::renderer::gl::OpenGlProgramUseDispatch {
|
||||
.use_program = use_opengl_program,
|
||||
});
|
||||
if (!status.ok())
|
||||
LOG("Shader::use() failed because: %s", status.message);
|
||||
}
|
||||
void Shader::u_vec4(kShaderUniform id, const glm::vec4& v)
|
||||
{
|
||||
if (m_umap.count(id) == 0)
|
||||
LOG("UNIFORM vec4 %d NOT FOUND in shader %d", (int)id, (int)name)
|
||||
else glUniform4fv(m_umap[id], 1, glm::value_ptr(v));
|
||||
else
|
||||
{
|
||||
const auto status = pp::renderer::gl::set_opengl_uniform_vec4(
|
||||
static_cast<std::int32_t>(m_umap[id]),
|
||||
glm::value_ptr(v),
|
||||
pp::renderer::gl::OpenGlUniformVec4Dispatch {
|
||||
.uniform_4fv = set_opengl_uniform_4fv,
|
||||
});
|
||||
if (!status.ok())
|
||||
LOG("Shader::u_vec4() failed because: %s", status.message);
|
||||
}
|
||||
}
|
||||
void Shader::u_vec3(kShaderUniform id, const glm::vec3& v)
|
||||
{
|
||||
if (m_umap.count(id) == 0)
|
||||
LOG("UNIFORM vec3 %d NOT FOUND in shader %d", (int)id, (int)name)
|
||||
else glUniform3fv(m_umap[id], 1, glm::value_ptr(v));
|
||||
else
|
||||
{
|
||||
const auto status = pp::renderer::gl::set_opengl_uniform_vec3(
|
||||
static_cast<std::int32_t>(m_umap[id]),
|
||||
glm::value_ptr(v),
|
||||
pp::renderer::gl::OpenGlUniformVec3Dispatch {
|
||||
.uniform_3fv = set_opengl_uniform_3fv,
|
||||
});
|
||||
if (!status.ok())
|
||||
LOG("Shader::u_vec3() failed because: %s", status.message);
|
||||
}
|
||||
}
|
||||
|
||||
void Shader::u_vec2(kShaderUniform id, const glm::vec2& v)
|
||||
{
|
||||
if (m_umap.count(id) == 0)
|
||||
LOG("UNIFORM vec2 %d NOT FOUND in shader %d", (int)id, (int)name)
|
||||
else glUniform2fv(m_umap[id], 1, glm::value_ptr(v));
|
||||
else
|
||||
{
|
||||
const auto status = pp::renderer::gl::set_opengl_uniform_vec2(
|
||||
static_cast<std::int32_t>(m_umap[id]),
|
||||
glm::value_ptr(v),
|
||||
pp::renderer::gl::OpenGlUniformVec2Dispatch {
|
||||
.uniform_2fv = set_opengl_uniform_2fv,
|
||||
});
|
||||
if (!status.ok())
|
||||
LOG("Shader::u_vec2() failed because: %s", status.message);
|
||||
}
|
||||
}
|
||||
|
||||
void Shader::u_mat4(kShaderUniform id, const glm::mat4& m)
|
||||
{
|
||||
if (m_umap.count(id) == 0)
|
||||
LOG("UNIFORM mat4 %d NOT FOUND in shader %d", (int)id, (int)name)
|
||||
else glUniformMatrix4fv(m_umap[id], 1, matrix_uniform_not_transposed(), glm::value_ptr(m));
|
||||
else
|
||||
{
|
||||
const auto status = pp::renderer::gl::set_opengl_uniform_mat4(
|
||||
static_cast<std::int32_t>(m_umap[id]),
|
||||
glm::value_ptr(m),
|
||||
pp::renderer::gl::OpenGlUniformMat4Dispatch {
|
||||
.uniform_matrix_4fv = set_opengl_uniform_matrix_4fv,
|
||||
});
|
||||
if (!status.ok())
|
||||
LOG("Shader::u_mat4() failed because: %s", status.message);
|
||||
}
|
||||
}
|
||||
void Shader::u_int(kShaderUniform id, int i)
|
||||
{
|
||||
if (m_umap.count(id) == 0)
|
||||
LOG("UNIFORM int %d NOT FOUND in shader %d", (int)id, (int)name)
|
||||
else
|
||||
glUniform1i(m_umap[id], i);
|
||||
{
|
||||
const auto status = pp::renderer::gl::set_opengl_uniform_int(
|
||||
static_cast<std::int32_t>(m_umap[id]),
|
||||
static_cast<std::int32_t>(i),
|
||||
pp::renderer::gl::OpenGlUniformIntDispatch {
|
||||
.uniform_1i = set_opengl_uniform_1i,
|
||||
});
|
||||
if (!status.ok())
|
||||
LOG("Shader::u_int() failed because: %s", status.message);
|
||||
}
|
||||
}
|
||||
void Shader::u_int(const char* uniform_name, int i)
|
||||
{
|
||||
glUniform1i(glGetAttribLocation(prog, uniform_name), i);
|
||||
const auto location = pp::renderer::gl::get_opengl_attribute_location(
|
||||
static_cast<std::uint32_t>(prog),
|
||||
uniform_name,
|
||||
pp::renderer::gl::OpenGlAttributeLocationDispatch {
|
||||
.get_attrib_location = get_opengl_attrib_location,
|
||||
});
|
||||
if (!location.ok())
|
||||
{
|
||||
LOG("Shader::u_int(name) lookup failed because: %s", location.status().message);
|
||||
return;
|
||||
}
|
||||
const auto status = pp::renderer::gl::set_opengl_uniform_int(
|
||||
location.value(),
|
||||
static_cast<std::int32_t>(i),
|
||||
pp::renderer::gl::OpenGlUniformIntDispatch {
|
||||
.uniform_1i = set_opengl_uniform_1i,
|
||||
});
|
||||
if (!status.ok())
|
||||
LOG("Shader::u_int(name) failed because: %s", status.message);
|
||||
}
|
||||
void Shader::u_float(kShaderUniform id, float f)
|
||||
{
|
||||
if (m_umap.count(id) == 0)
|
||||
LOG("UNIFORM float %d NOT FOUND in shader %d", (int)id, (int)name)
|
||||
else glUniform1f(m_umap[id], f);
|
||||
else
|
||||
{
|
||||
const auto status = pp::renderer::gl::set_opengl_uniform_float(
|
||||
static_cast<std::int32_t>(m_umap[id]),
|
||||
f,
|
||||
pp::renderer::gl::OpenGlUniformFloatDispatch {
|
||||
.uniform_1f = set_opengl_uniform_1f,
|
||||
});
|
||||
if (!status.ok())
|
||||
LOG("Shader::u_float() failed because: %s", status.message);
|
||||
}
|
||||
}
|
||||
GLint Shader::GetAttribLocation(const char* attribute_name)
|
||||
{
|
||||
return glGetAttribLocation(prog, attribute_name);
|
||||
const auto location = pp::renderer::gl::get_opengl_attribute_location(
|
||||
static_cast<std::uint32_t>(prog),
|
||||
attribute_name,
|
||||
pp::renderer::gl::OpenGlAttributeLocationDispatch {
|
||||
.get_attrib_location = get_opengl_attrib_location,
|
||||
});
|
||||
if (!location.ok())
|
||||
{
|
||||
LOG("Shader::GetAttribLocation() failed because: %s", location.status().message);
|
||||
return -1;
|
||||
}
|
||||
return static_cast<GLint>(location.value());
|
||||
}
|
||||
|
||||
bool ShaderManager::load(kShader id, const std::string& path)
|
||||
|
||||
Reference in New Issue
Block a user