Commit ee5a9c78 authored by Aldo Culquicondor's avatar Aldo Culquicondor Committed by Commit Bot

VR: Ability to clip elements

It affects drawing and hit testing. If the quad is completely outside the clip, nothing is sent to the CPU.
This is going to be used to implement scrolling.
Also, this CL removes the unused copy_rect.

Bug: 839488
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_vr
Change-Id: I07c087f3d7860456587de092797349851c26502a
Reviewed-on: https://chromium-review.googlesource.com/1054371
Commit-Queue: Aldo Culquicondor <acondor@chromium.org>
Reviewed-by: default avatarChristopher Grant <cjgrant@chromium.org>
Cr-Commit-Position: refs/heads/master@{#558021}
parent f812c075
......@@ -46,11 +46,10 @@ ContentElement::~ContentElement() = default;
void ContentElement::Render(UiElementRenderer* renderer,
const CameraModel& model) const {
gfx::RectF copy_rect(0, 0, 1, 1);
if (uses_quad_layer_) {
renderer->DrawTexturedQuad(0, 0, texture_location(),
model.view_proj_matrix * world_space_transform(),
copy_rect, computed_opacity(), size(),
clip_rect(), computed_opacity(), size(),
corner_radius(), false);
return;
}
......@@ -60,7 +59,7 @@ void ContentElement::Render(UiElementRenderer* renderer,
if (texture_id() || overlay_texture_id) {
renderer->DrawTexturedQuad(
texture_id(), overlay_texture_id, texture_location(),
model.view_proj_matrix * world_space_transform(), copy_rect,
model.view_proj_matrix * world_space_transform(), clip_rect(),
computed_opacity(), size(), corner_radius(), true);
}
}
......
......@@ -16,7 +16,7 @@ void FullScreenRect::Render(UiElementRenderer* renderer,
const CameraModel& model) const {
gfx::Transform m;
m.Scale3d(2.0f, 2.0f, 1.0f);
renderer->DrawGradientQuad(m, edge_color(), center_color(),
renderer->DrawGradientQuad(m, edge_color(), center_color(), clip_rect(),
computed_opacity(), gfx::SizeF(1.f, 1.f),
corner_radii());
}
......
......@@ -45,8 +45,8 @@ void Rect::Render(UiElementRenderer* renderer, const CameraModel& model) const {
if (opacity <= 0.f)
return;
renderer->DrawGradientQuad(model.view_proj_matrix * world_space_transform(),
edge_color_, center_color_, opacity, size(),
corner_radii());
edge_color_, center_color_, clip_rect(), opacity,
size(), corner_radii());
}
void Rect::SetLocalOpacity(float opacity) {
......
......@@ -83,10 +83,9 @@ void TexturedElement::Render(UiElementRenderer* renderer,
if (texture_handle_ <= 0)
return;
gfx::RectF copy_rect(0, 0, 1, 1);
renderer->DrawTexturedQuad(
texture_handle_, 0, UiElementRenderer::kTextureLocationLocal,
model.view_proj_matrix * world_space_transform(), copy_rect,
model.view_proj_matrix * world_space_transform(), clip_rect(),
computed_opacity(), size(), corner_radius(), true /* blend */);
}
......
......@@ -13,6 +13,7 @@
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "chrome/browser/vr/model/camera_model.h"
#include "chrome/browser/vr/vr_gl_util.h"
#include "third_party/blink/public/platform/web_gesture_event.h"
#include "third_party/skia/include/core/SkRRect.h"
#include "third_party/skia/include/core/SkRect.h"
......@@ -466,9 +467,14 @@ bool UiElement::LocalHitTest(const gfx::PointF& point) const {
if (point.x() < 0.0f || point.x() > 1.0f || point.y() < 0.0f ||
point.y() > 1.0f) {
return false;
} else if (corner_radii_.IsZero()) {
return point.x() >= 0.0f && point.x() <= 1.0f && point.y() >= 0.0f &&
point.y() <= 1.0f;
}
if (!clip_rect_.IsEmpty() &&
!CalculateTexSpaceRect(size(), clip_rect_).Contains(point)) {
return false;
}
if (corner_radii_.IsZero()) {
return true;
}
float width = size().width();
......@@ -806,6 +812,11 @@ bool UiElement::SizeAndLayOut() {
changed |= PrepareToDraw();
set_update_phase(kUpdatedSize);
DoLayOutChildren();
if (clips_descendants_) {
clip_rect_ = {-0.5f * size().width(), 0.5f * size().height(),
size().width(), size().height()};
ClipChildren();
}
set_update_phase(kUpdatedLayout);
return changed;
}
......@@ -910,6 +921,21 @@ void UiElement::LayOutChildren() {
}
}
void UiElement::ClipChildren() {
for (auto& child : children_) {
// Nested clipping is not supported yet.
DCHECK(!child->clips_descendants_);
if (!child->IsVisible())
continue;
DCHECK(child->LocalTransform().IsScaleOrTranslation());
child->clip_rect_ = clip_rect_;
child->LocalTransform().TransformRectReverse(&child->clip_rect_);
child->ClipChildren();
}
}
UiElement* UiElement::FirstLaidOutChild() const {
auto i = std::find_if(
children_.begin(), children_.end(),
......
......@@ -166,7 +166,7 @@ class UiElement : public cc::AnimationTarget {
// shapes. Points within the rectangular area are mapped from 0:1 as follows,
// though will extend outside this range when outside of the element:
// [(0.0, 0.0), (1.0, 0.0)
// (1.0, 0.0), (1.0, 1.0)]
// (0.0, 1.0), (1.0, 1.0)]
virtual bool LocalHitTest(const gfx::PointF& point) const;
// Performs a hit test for the ray supplied in the request and populates the
......@@ -225,6 +225,9 @@ class UiElement : public cc::AnimationTarget {
void SetSize(float width, float hight);
virtual void OnSetSize(const gfx::SizeF& size);
gfx::RectF clip_rect() const { return clip_rect_; }
void set_clip_rect_for_test(const gfx::RectF& rect) { clip_rect_ = rect; }
gfx::PointF local_origin() const { return local_origin_; }
// These are convenience functions for setting the transform operations. They
......@@ -412,6 +415,10 @@ class UiElement : public cc::AnimationTarget {
// applies anchoring.
virtual void LayOutChildren();
// Recursive method that clips element subtrees, given that a parent is
// clipped.
void ClipChildren();
UiElement* FirstLaidOutChild() const;
UiElement* LastLaidOutChild() const;
......@@ -462,7 +469,10 @@ class UiElement : public cc::AnimationTarget {
// that override element hover and click methods must manage their own sounds.
void SetSounds(Sounds sounds, AudioDelegate* delegate);
bool resizable_by_layout() { return resizable_by_layout_; }
bool clips_descendants() const { return clips_descendants_; }
void set_clip_descendants(bool clips) { clips_descendants_ = clips; }
bool resizable_by_layout() const { return resizable_by_layout_; }
void set_resizable_by_layout(bool resizable) {
resizable_by_layout_ = resizable;
}
......@@ -518,6 +528,13 @@ class UiElement : public cc::AnimationTarget {
// The size of the object. This does not affect children.
gfx::SizeF size_;
// The clip of the object. The rect is in relation to the element's size,
// with the origin at its center.
gfx::RectF clip_rect_;
// Indicates that this element clips its descendants with its size.
bool clips_descendants_ = false;
// The local orgin of the element. This can be updated, say, so that an
// element can contain its children, even if they are not centered about its
// true origin.
......
......@@ -333,7 +333,7 @@ TEST(UiElement, HitTest) {
{gfx::PointF(-0.1f, -0.1f), false, false, false},
};
for (size_t i = 0; i < arraysize(test_cases); ++i) {
for (size_t i = 0; i < base::size(test_cases); ++i) {
SCOPED_TRACE(i);
EXPECT_EQ(test_cases[i].expected_rect,
rect.LocalHitTest(test_cases[i].location));
......@@ -344,6 +344,37 @@ TEST(UiElement, HitTest) {
}
}
TEST(UiElement, HitTestWithClip) {
UiElement rect;
rect.SetSize(1.0, 1.0);
// A horizontal band in the middle.
rect.set_clip_rect_for_test({-0.5f, 0.2f, 1.0f, 0.4f});
struct {
gfx::PointF location;
bool expected;
} test_cases[] = {
// Vertical walk.
{{0.5f, 0.0f}, false},
{{0.5f, 0.2f}, false},
{{0.5f, 0.4f}, true},
{{0.5f, 0.6f}, true},
{{0.5f, 0.8f}, false},
{{0.5f, 1.0f}, false},
// Horizontal walk.
{{0.0f, 0.5f}, true},
{{0.2f, 0.5f}, true},
{{0.4f, 0.5f}, true},
{{0.6f, 0.5f}, true},
{{0.8f, 0.5f}, true},
};
for (size_t i = 0; i < base::size(test_cases); ++i) {
SCOPED_TRACE(i);
EXPECT_EQ(test_cases[i].expected,
rect.LocalHitTest(test_cases[i].location));
}
}
class ElementEventHandlers {
public:
explicit ElementEventHandlers(UiElement* element) {
......@@ -429,4 +460,27 @@ TEST(UiElement, EventBubbling) {
element_handlers.ExpectCalled(false);
}
// The clip rect is properly transformed into the child's coordinates.
TEST(UiElement, ClipChildren) {
auto parent = std::make_unique<UiElement>();
parent->SetSize(16.0f, 8.0f);
parent->set_clip_descendants(true);
auto child = std::make_unique<UiElement>();
child->SetSize(4.0f, 4.0f);
child->set_y_anchoring(TOP);
auto* p_child = child.get();
parent->AddChild(std::move(child));
parent->SizeAndLayOut();
EXPECT_FLOAT_RECT_EQ(gfx::RectF(-8.0f, 0.0f, 16.0f, 8.0f),
p_child->clip_rect());
p_child->SetScale(0.5f, 0.5f, 1.0f);
parent->DoLayOutChildren();
parent->ClipChildren();
EXPECT_FLOAT_RECT_EQ(gfx::RectF(-16.0f, 0.0f, 32.0f, 16.0f),
p_child->clip_rect());
}
} // namespace vr
......@@ -27,6 +27,7 @@ BaseRenderer::BaseRenderer(const char* vertex_src, const char* fragment_src) {
glDeleteShader(fragment_shader_handle);
position_handle_ = glGetAttribLocation(program_handle_, "a_Position");
clip_rect_handle_ = glGetUniformLocation(program_handle_, "u_ClipRect");
}
BaseRenderer::~BaseRenderer() = default;
......
......@@ -23,6 +23,8 @@ class BaseRenderer {
GLuint program_handle_ = 0;
GLuint position_handle_ = 0;
GLuint clip_rect_handle_ = 0;
DISALLOW_COPY_AND_ASSIGN(BaseRenderer);
};
......
......@@ -7,6 +7,7 @@
#include "chrome/browser/vr/elements/corner_radii.h"
#include "chrome/browser/vr/renderers/textured_quad_renderer.h"
#include "chrome/browser/vr/vr_gl_util.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size_f.h"
#include "ui/gfx/transform.h"
......@@ -37,6 +38,7 @@ static constexpr char const* kVertexShader = SHADER(
attribute vec2 a_CornerPosition;
attribute vec2 a_OffsetScale;
varying vec2 v_CornerPosition;
varying vec2 v_TexCoordinate;
varying vec2 v_Position;
void main() {
......@@ -61,6 +63,7 @@ static constexpr char const* kVertexShader = SHADER(
} else {
v_Position = vec2(position.x * u_AspectRatio, position.y);
}
v_TexCoordinate = vec2(0.5 + position[0], 0.5 - position[1]);
gl_Position = u_ModelViewProjMatrix * position;
}
);
......@@ -69,10 +72,17 @@ static constexpr char const* kFragmentShader = SHADER(
precision highp float;
varying vec2 v_CornerPosition;
varying vec2 v_Position;
varying vec2 v_TexCoordinate;
uniform mediump float u_Opacity;
uniform vec2 u_ClipRect[2];
uniform vec4 u_CenterColor;
uniform vec4 u_EdgeColor;
void main() {
vec2 s = step(u_ClipRect[0], v_TexCoordinate)
- step(u_ClipRect[1], v_TexCoordinate);
float insideClip = s.x * s.y;
vec2 position = v_Position;
float edge_color_weight = clamp(2.0 * length(position), 0.0, 1.0);
float center_color_weight = 1.0 - edge_color_weight;
......@@ -84,7 +94,7 @@ static constexpr char const* kFragmentShader = SHADER(
color = color + n;
color = vec4(color.rgb * color.a, color.a);
gl_FragColor = color * u_Opacity * mask;
gl_FragColor = insideClip * color * u_Opacity * mask;
}
);
// clang-format on
......@@ -124,6 +134,7 @@ GradientQuadRenderer::~GradientQuadRenderer() = default;
void GradientQuadRenderer::Draw(const gfx::Transform& model_view_proj_matrix,
SkColor edge_color,
SkColor center_color,
const gfx::RectF& clip_rect,
float opacity,
const gfx::SizeF& element_size,
const CornerRadii& radii) {
......@@ -132,6 +143,9 @@ void GradientQuadRenderer::Draw(const gfx::Transform& model_view_proj_matrix,
SkColorGetA(center_color) == SK_AlphaTRANSPARENT) {
return;
}
auto tex_clip_rect = CalculateTexSpaceRect(element_size, clip_rect);
if (!tex_clip_rect.Intersects({0.0f, 0.0f, 1.0f, 1.0f}))
return;
glUseProgram(program_handle_);
......@@ -171,6 +185,11 @@ void GradientQuadRenderer::Draw(const gfx::Transform& model_view_proj_matrix,
glUniformMatrix4fv(model_view_proj_matrix_handle_, 1, false,
MatrixToGLArray(model_view_proj_matrix).data());
const GLfloat clip_rect_data[4] = {tex_clip_rect.x(), tex_clip_rect.y(),
tex_clip_rect.right(),
tex_clip_rect.bottom()};
glUniform2fv(clip_rect_handle_, 2, clip_rect_data);
if (radii.IsZero()) {
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
VOID_OFFSET(kInnerRectOffset));
......
......@@ -13,6 +13,7 @@
namespace gfx {
class SizeF;
class Transform;
class RectF;
} // namespace gfx
namespace vr {
......@@ -27,6 +28,7 @@ class GradientQuadRenderer : public BaseRenderer {
void Draw(const gfx::Transform& model_view_proj_matrix,
SkColor edge_color,
SkColor center_color,
const gfx::RectF& clip_rect,
float opacity,
const gfx::SizeF& element_size,
const CornerRadii& radii);
......
......@@ -142,18 +142,20 @@ static constexpr char const* kFragmentShader = SHADER(
precision highp float;
uniform sampler2D u_Texture;
uniform sampler2D u_OverlayTexture;
uniform vec4 u_CopyRect;
uniform vec2 u_ClipRect[2];
varying vec2 v_TexCoordinate;
varying vec2 v_CornerPosition;
uniform mediump float u_Opacity;
uniform mediump float u_OverlayOpacity;
void main() {
vec2 scaledTex =
vec2(u_CopyRect[0] + v_TexCoordinate.x * u_CopyRect[2],
u_CopyRect[1] + v_TexCoordinate.y * u_CopyRect[3]);
lowp vec4 color = texture2D(u_Texture, scaledTex);
vec2 s = step(u_ClipRect[0], v_TexCoordinate)
- step(u_ClipRect[1], v_TexCoordinate);
float insideClip = s.x * s.y;
lowp vec4 color = texture2D(u_Texture, v_TexCoordinate);
float mask = 1.0 - step(1.0, length(v_CornerPosition));
gl_FragColor = color * u_Opacity * mask;
gl_FragColor = insideClip * color * u_Opacity * mask;
}
);
// clang-format on
......@@ -174,8 +176,6 @@ TexturedQuadRenderer::TexturedQuadRenderer(const char* vertex_src,
glGetAttribLocation(program_handle_, "a_CornerPosition");
offset_scale_handle_ = glGetAttribLocation(program_handle_, "a_OffsetScale");
copy_rect_handler_ = glGetUniformLocation(program_handle_, "u_CopyRect");
opacity_handle_ = glGetUniformLocation(program_handle_, "u_Opacity");
overlay_opacity_handle_ =
glGetUniformLocation(program_handle_, "u_OverlayOpacity");
......@@ -190,7 +190,7 @@ TexturedQuadRenderer::~TexturedQuadRenderer() = default;
void TexturedQuadRenderer::AddQuad(int texture_data_handle,
int overlay_texture_data_handle,
const gfx::Transform& model_view_proj_matrix,
const gfx::RectF& copy_rect,
const gfx::RectF& clip_rect,
float opacity,
const gfx::SizeF& element_size,
float corner_radius,
......@@ -199,12 +199,13 @@ void TexturedQuadRenderer::AddQuad(int texture_data_handle,
quad.texture_data_handle = texture_data_handle;
quad.overlay_texture_data_handle = overlay_texture_data_handle;
quad.model_view_proj_matrix = model_view_proj_matrix;
quad.copy_rect = copy_rect;
quad.clip_rect = CalculateTexSpaceRect(element_size, clip_rect);
quad.opacity = opacity;
quad.element_size = element_size;
quad.corner_radius = corner_radius;
quad.blend = blend;
quad_queue_.push(quad);
if (quad.clip_rect.Intersects({0.0f, 0.0f, 1.0f, 1.0f}))
quad_queue_.push(quad);
}
void TexturedQuadRenderer::Flush() {
......@@ -216,7 +217,7 @@ void TexturedQuadRenderer::Flush() {
float last_opacity = -1.0f;
gfx::SizeF last_element_size;
float last_corner_radius = -1.0f;
gfx::RectF last_copy_rect;
gfx::RectF last_clip_rect;
bool last_blend = true; // All elements blend by default.
// Set up GL state that doesn't change between draw calls.
......@@ -306,10 +307,12 @@ void TexturedQuadRenderer::Flush() {
glUniformMatrix4fv(model_view_proj_matrix_handle_, 1, false,
MatrixToGLArray(quad.model_view_proj_matrix).data());
if (last_copy_rect != quad.copy_rect) {
last_copy_rect = quad.copy_rect;
glUniform4f(copy_rect_handler_, quad.copy_rect.x(), quad.copy_rect.y(),
quad.copy_rect.width(), quad.copy_rect.height());
if (last_clip_rect != quad.clip_rect) {
last_clip_rect = quad.clip_rect;
const GLfloat clip_rect_data[4] = {quad.clip_rect.x(), quad.clip_rect.y(),
quad.clip_rect.right(),
quad.clip_rect.bottom()};
glUniform2fv(clip_rect_handle_, 2, clip_rect_data);
}
if (quad.corner_radius == 0.0f) {
......
......@@ -26,7 +26,7 @@ class TexturedQuadRenderer : public BaseRenderer {
void AddQuad(int texture_data_handle,
int overlay_texture_data_handle,
const gfx::Transform& model_view_proj_matrix,
const gfx::RectF& copy_rect,
const gfx::RectF& clip_rect,
float opacity,
const gfx::SizeF& element_size,
float corner_radius,
......@@ -48,7 +48,7 @@ class TexturedQuadRenderer : public BaseRenderer {
int texture_data_handle;
int overlay_texture_data_handle;
gfx::Transform model_view_proj_matrix;
gfx::RectF copy_rect;
gfx::RectF clip_rect;
float opacity;
gfx::SizeF element_size;
float corner_radius;
......@@ -65,7 +65,6 @@ class TexturedQuadRenderer : public BaseRenderer {
GLuint overlay_opacity_handle_;
GLuint texture_handle_;
GLuint overlay_texture_handle_;
GLuint copy_rect_handler_;
// Attributes
GLuint corner_position_handle_;
......
......@@ -16,7 +16,7 @@ void FakeUiElementRenderer::DrawTexturedQuad(
int overlay_texture_data_handle,
TextureLocation texture_location,
const gfx::Transform& view_proj_matrix,
const gfx::RectF& copy_rect,
const gfx::RectF& clip_rect,
float opacity,
const gfx::SizeF& element_size,
float corner_radius,
......@@ -29,6 +29,7 @@ void FakeUiElementRenderer::DrawGradientQuad(
const gfx::Transform& view_proj_matrix,
const SkColor edge_color,
const SkColor center_color,
const gfx::RectF& clip_rect,
float opacity,
const gfx::SizeF& element_size,
const CornerRadii& corner_radii) {
......
......@@ -22,7 +22,7 @@ class FakeUiElementRenderer : public UiElementRenderer {
int overlay_texture_data_handle,
TextureLocation texture_location,
const gfx::Transform& view_proj_matrix,
const gfx::RectF& copy_rect,
const gfx::RectF& clip_rect,
float opacity,
const gfx::SizeF& element_size,
float corner_radius,
......@@ -31,6 +31,7 @@ class FakeUiElementRenderer : public UiElementRenderer {
void DrawGradientQuad(const gfx::Transform& view_proj_matrix,
const SkColor edge_color,
const SkColor center_color,
const gfx::RectF& clip_rect,
float opacity,
const gfx::SizeF& element_size,
const CornerRadii& corner_radii) override;
......
......@@ -58,7 +58,7 @@ void UiElementRenderer::DrawTexturedQuad(
int overlay_texture_data_handle,
TextureLocation texture_location,
const gfx::Transform& model_view_proj_matrix,
const gfx::RectF& copy_rect,
const gfx::RectF& clip_rect,
float opacity,
const gfx::SizeF& element_size,
float corner_radius,
......@@ -79,7 +79,7 @@ void UiElementRenderer::DrawTexturedQuad(
}
FlushIfNecessary(renderer);
renderer->AddQuad(texture_data_handle, overlay_texture_data_handle,
model_view_proj_matrix, copy_rect, opacity, element_size,
model_view_proj_matrix, clip_rect, opacity, element_size,
corner_radius, blend);
}
......@@ -87,13 +87,15 @@ void UiElementRenderer::DrawGradientQuad(
const gfx::Transform& model_view_proj_matrix,
const SkColor edge_color,
const SkColor center_color,
const gfx::RectF& clip_rect,
float opacity,
const gfx::SizeF& element_size,
const CornerRadii& radii) {
TRACE_EVENT0("gpu", "UiElementRenderer::DrawGradientQuad");
FlushIfNecessary(gradient_quad_renderer_.get());
gradient_quad_renderer_->Draw(model_view_proj_matrix, edge_color,
center_color, opacity, element_size, radii);
center_color, clip_rect, opacity, element_size,
radii);
}
void UiElementRenderer::DrawGradientGridQuad(
......
......@@ -64,7 +64,7 @@ class UiElementRenderer {
int overlay_texture_data_handle,
TextureLocation texture_location,
const gfx::Transform& model_view_proj_matrix,
const gfx::RectF& copy_rect,
const gfx::RectF& clip_rect,
float opacity,
const gfx::SizeF& element_size,
float corner_radius,
......@@ -73,6 +73,7 @@ class UiElementRenderer {
const gfx::Transform& model_view_proj_matrix,
const SkColor edge_color,
const SkColor center_color,
const gfx::RectF& clip_rect,
float opacity,
const gfx::SizeF& element_size,
const CornerRadii& radii);
......
......@@ -27,6 +27,19 @@ gfx::Rect CalculatePixelSpaceRect(const gfx::Size& texture_size,
return gfx::Rect(rect.x(), rect.y(), rect.width(), rect.height());
}
gfx::RectF CalculateTexSpaceRect(const gfx::SizeF& element_size,
const gfx::RectF& rect) {
if (rect.IsEmpty())
return {0.0f, 0.0f, 1.0f, 1.0f};
auto new_origin =
rect.origin() - gfx::Vector2dF(-element_size.width() * 0.5f,
element_size.height() * 0.5f);
new_origin.set_y(-new_origin.y());
gfx::RectF result(new_origin, rect.size());
result.Scale(1.0f / element_size.width(), 1.0f / element_size.height());
return result;
}
GLuint CompileShader(GLenum shader_type,
const GLchar* shader_source,
std::string& error) {
......
......@@ -31,6 +31,9 @@ std::array<float, 16> MatrixToGLArray(const gfx::Transform& matrix);
gfx::Rect CalculatePixelSpaceRect(const gfx::Size& texture_size,
const gfx::RectF& texture_rect);
gfx::RectF CalculateTexSpaceRect(const gfx::SizeF& element_size,
const gfx::RectF& rect);
// Compile a shader.
GLuint CompileShader(GLenum shader_type,
const GLchar* shader_source,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment