1 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "gpu/command_buffer/service/transform_feedback_manager.h"
6 
7 #include "base/numerics/checked_math.h"
8 #include "gpu/command_buffer/service/buffer_manager.h"
9 #include "ui/gl/gl_version_info.h"
10 
11 namespace gpu {
12 namespace gles2 {
13 
TransformFeedback(TransformFeedbackManager * manager,GLuint client_id,GLuint service_id)14 TransformFeedback::TransformFeedback(TransformFeedbackManager* manager,
15                                      GLuint client_id,
16                                      GLuint service_id)
17     : IndexedBufferBindingHost(
18           manager->max_transform_feedback_separate_attribs(),
19           GL_TRANSFORM_FEEDBACK_BUFFER,
20           manager->needs_emulation(),
21           false),
22       manager_(manager),
23       client_id_(client_id),
24       service_id_(service_id),
25       has_been_bound_(false),
26       active_(false),
27       paused_(false),
28       primitive_mode_(GL_NONE),
29       vertices_drawn_(0) {
30   DCHECK_LE(0u, client_id);
31   DCHECK_LT(0u, service_id);
32 }
33 
~TransformFeedback()34 TransformFeedback::~TransformFeedback() {
35   if (!manager_->lost_context()) {
36     if (active_)
37       glEndTransformFeedback();
38     glDeleteTransformFeedbacks(1, &service_id_);
39   }
40 }
41 
DoBindTransformFeedback(GLenum target,TransformFeedback * last_bound_transform_feedback,Buffer * bound_transform_feedback_buffer)42 void TransformFeedback::DoBindTransformFeedback(
43     GLenum target,
44     TransformFeedback* last_bound_transform_feedback,
45     Buffer* bound_transform_feedback_buffer) {
46   DCHECK_LT(0u, service_id_);
47   glBindTransformFeedback(target, service_id_);
48   // GL drivers differ on whether GL_TRANSFORM_FEEDBACK_BUFFER is changed
49   // when calling glBindTransformFeedback. To make them consistent, we
50   // explicitly bind the buffer we think should be bound here.
51   if (bound_transform_feedback_buffer &&
52       !bound_transform_feedback_buffer->IsDeleted()) {
53     glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER,
54                  bound_transform_feedback_buffer->service_id());
55   } else {
56     glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);
57   }
58   has_been_bound_ = true;
59   if (active_ && !paused_) {
60     // This could only happen during virtual context switching.
61     // Otherwise the validation should generate a GL error without calling
62     // into this function.
63     glResumeTransformFeedback();
64   }
65   if (last_bound_transform_feedback != this) {
66     if (last_bound_transform_feedback) {
67       last_bound_transform_feedback->SetIsBound(false);
68     }
69     SetIsBound(true);
70   }
71 }
72 
DoBeginTransformFeedback(GLenum primitive_mode)73 void TransformFeedback::DoBeginTransformFeedback(GLenum primitive_mode) {
74   DCHECK(!active_);
75   DCHECK(primitive_mode == GL_POINTS ||
76          primitive_mode == GL_LINES ||
77          primitive_mode == GL_TRIANGLES);
78   glBeginTransformFeedback(primitive_mode);
79   active_ = true;
80   primitive_mode_ = primitive_mode;
81   vertices_drawn_ = 0;
82 }
83 
DoEndTransformFeedback()84 void TransformFeedback::DoEndTransformFeedback() {
85   DCHECK(active_);
86   glEndTransformFeedback();
87   active_ = false;
88   paused_ = false;
89 }
90 
DoPauseTransformFeedback()91 void TransformFeedback::DoPauseTransformFeedback() {
92   DCHECK(active_ && !paused_);
93   glPauseTransformFeedback();
94   paused_ = true;
95 }
96 
DoResumeTransformFeedback()97 void TransformFeedback::DoResumeTransformFeedback() {
98   DCHECK(active_ && paused_);
99   glResumeTransformFeedback();
100   paused_ = false;
101 }
102 
GetVerticesNeededForDraw(GLenum mode,GLsizei count,GLsizei primcount,GLsizei pending_vertices_drawn,GLsizei * vertices_out) const103 bool TransformFeedback::GetVerticesNeededForDraw(GLenum mode,
104                                                  GLsizei count,
105                                                  GLsizei primcount,
106                                                  GLsizei pending_vertices_drawn,
107                                                  GLsizei* vertices_out) const {
108   // Transform feedback only outputs complete primitives, so we need to round
109   // down to the nearest complete primitive before multiplying by the number of
110   // instances.
111   base::CheckedNumeric<GLsizei> checked_vertices =
112       vertices_drawn_ + pending_vertices_drawn;
113   base::CheckedNumeric<GLsizei> checked_count = count;
114   base::CheckedNumeric<GLsizei> checked_primcount = primcount;
115   switch (mode) {
116     case GL_TRIANGLES:
117       checked_vertices +=
118           checked_primcount * (checked_count - checked_count % 3);
119       break;
120     case GL_LINES:
121       checked_vertices +=
122           checked_primcount * (checked_count - checked_count % 2);
123       break;
124     default:
125       NOTREACHED();
126       FALLTHROUGH;
127     case GL_POINTS:
128       checked_vertices += checked_primcount * checked_count;
129   }
130   // Note that the use of GLsizei limits us to 32-bit numbers of vertices. If
131   // the command buffer is upgraded to allow 64-bit buffer sizes then this
132   // should be expanded to 64 bits as well.
133   *vertices_out = checked_vertices.ValueOrDefault(0);
134   return checked_vertices.IsValid();
135 }
136 
OnVerticesDrawn(GLsizei vertices_drawn)137 void TransformFeedback::OnVerticesDrawn(GLsizei vertices_drawn) {
138   if (active_ && !paused_) {
139     vertices_drawn_ = vertices_drawn;
140   }
141 }
142 
TransformFeedbackManager(GLuint max_transform_feedback_separate_attribs,bool needs_emulation)143 TransformFeedbackManager::TransformFeedbackManager(
144     GLuint max_transform_feedback_separate_attribs,
145     bool needs_emulation)
146     : max_transform_feedback_separate_attribs_(
147           max_transform_feedback_separate_attribs),
148       needs_emulation_(needs_emulation),
149       lost_context_(false) {
150   DCHECK(needs_emulation);
151 }
152 
~TransformFeedbackManager()153 TransformFeedbackManager::~TransformFeedbackManager() {
154   DCHECK(transform_feedbacks_.empty());
155 }
156 
Destroy()157 void TransformFeedbackManager::Destroy() {
158   transform_feedbacks_.clear();
159 }
160 
CreateTransformFeedback(GLuint client_id,GLuint service_id)161 TransformFeedback* TransformFeedbackManager::CreateTransformFeedback(
162     GLuint client_id, GLuint service_id) {
163   scoped_refptr<TransformFeedback> transform_feedback(
164       new TransformFeedback(this, client_id, service_id));
165   auto result = transform_feedbacks_.insert(
166       std::make_pair(client_id, transform_feedback));
167   DCHECK(result.second);
168   return result.first->second.get();
169 }
170 
GetTransformFeedback(GLuint client_id)171 TransformFeedback* TransformFeedbackManager::GetTransformFeedback(
172     GLuint client_id) {
173   if (client_id == 0) {
174     return nullptr;
175   }
176   auto it = transform_feedbacks_.find(client_id);
177   return it != transform_feedbacks_.end() ? it->second.get() : nullptr;
178 }
179 
RemoveTransformFeedback(GLuint client_id)180 void TransformFeedbackManager::RemoveTransformFeedback(GLuint client_id) {
181   if (client_id) {
182     transform_feedbacks_.erase(client_id);
183   }
184 }
185 
186 }  // namespace gles2
187 }  // namespace gpu
188