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