Index: gpu/command_buffer/service/gles2_cmd_decoder.cc |
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc |
index fd688cf290519a478f28d15c2dc0bf35f6d9152e..d233b3251ac497504504d5e4cc467dfa86a3f7c9 100644 |
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc |
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc |
@@ -56,6 +56,7 @@ |
#include "gpu/command_buffer/service/shader_manager.h" |
#include "gpu/command_buffer/service/shader_translator.h" |
#include "gpu/command_buffer/service/texture_manager.h" |
+#include "gpu/command_buffer/service/transform_feedback_manager.h" |
#include "gpu/command_buffer/service/vertex_array_manager.h" |
#include "gpu/command_buffer/service/vertex_attrib_manager.h" |
#include "third_party/smhasher/src/City.h" |
@@ -630,6 +631,9 @@ class GLES2DecoderImpl : public GLES2Decoder, public ErrorStateClient { |
void RestoreAllAttributes() const override; |
QueryManager* GetQueryManager() override { return query_manager_.get(); } |
+ TransformFeedbackManager* GetTransformFeedbackManager() override { |
+ return transform_feedback_manager_.get(); |
+ } |
VertexArrayManager* GetVertexArrayManager() override { |
return vertex_array_manager_.get(); |
} |
@@ -752,6 +756,8 @@ class GLES2DecoderImpl : public GLES2Decoder, public ErrorStateClient { |
bool DeletePathsCHROMIUMHelper(GLuint first_client_id, GLsizei range); |
bool GenSamplersHelper(GLsizei n, const GLuint* client_ids); |
void DeleteSamplersHelper(GLsizei n, const GLuint* client_ids); |
+ bool GenTransformFeedbacksHelper(GLsizei n, const GLuint* client_ids); |
+ void DeleteTransformFeedbacksHelper(GLsizei n, const GLuint* client_ids); |
// Workarounds |
void OnFboChanged() const; |
@@ -855,6 +861,24 @@ class GLES2DecoderImpl : public GLES2Decoder, public ErrorStateClient { |
sampler_manager()->RemoveSampler(client_id); |
} |
+ // Creates a TransformFeedback for the given transformfeedback. |
+ TransformFeedback* CreateTransformFeedback( |
+ GLuint client_id, GLuint service_id) { |
+ return transform_feedback_manager_->CreateTransformFeedback( |
+ client_id, service_id); |
+ } |
+ |
+ // Gets the TransformFeedback info for the given transformfeedback. |
+ // Returns nullptr if none exists. |
+ TransformFeedback* GetTransformFeedback(GLuint client_id) { |
+ return transform_feedback_manager_->GetTransformFeedback(client_id); |
+ } |
+ |
+ // Deletes the TransformFeedback info for the given transformfeedback. |
+ void RemoveTransformFeedback(GLuint client_id) { |
+ transform_feedback_manager_->RemoveTransformFeedback(client_id); |
+ } |
+ |
// Get the size (in pixels) of the currently bound frame buffer (either FBO |
// or regular back buffer). |
gfx::Size GetBoundReadFrameBufferSize(); |
@@ -1375,6 +1399,22 @@ class GLES2DecoderImpl : public GLES2Decoder, public ErrorStateClient { |
// Wrapper for glBindSampler since we need to track the current targets. |
void DoBindSampler(GLuint unit, GLuint sampler); |
+ // Wrapper for glBindTransformFeedback since we need to emulate ES3 behaviors |
+ // for BindBufferRange on Desktop GL lower than 4.2. |
+ void DoBindTransformFeedback(GLenum target, GLuint transform_feedback); |
+ |
+ // Wrapper for glBeginTransformFeedback. |
+ void DoBeginTransformFeedback(GLenum primitive_mode); |
+ |
+ // Wrapper for glEndTransformFeedback. |
+ void DoEndTransformFeedback(); |
+ |
+ // Wrapper for glPauseTransformFeedback. |
+ void DoPauseTransformFeedback(); |
+ |
+ // Wrapper for glResumeTransformFeedback. |
+ void DoResumeTransformFeedback(); |
+ |
// Wrapper for glBindVertexArrayOES |
void DoBindVertexArrayOES(GLuint array); |
void EmulateVertexArrayState(); |
@@ -1550,6 +1590,7 @@ class GLES2DecoderImpl : public GLES2Decoder, public ErrorStateClient { |
bool DoIsShader(GLuint client_id); |
bool DoIsTexture(GLuint client_id); |
bool DoIsSampler(GLuint client_id); |
+ bool DoIsTransformFeedback(GLuint client_id); |
bool DoIsVertexArrayOES(GLuint client_id); |
bool DoIsPathCHROMIUM(GLuint client_id); |
@@ -1954,6 +1995,8 @@ class GLES2DecoderImpl : public GLES2Decoder, public ErrorStateClient { |
// All the state for this context. |
ContextState state_; |
+ std::unique_ptr<TransformFeedbackManager> transform_feedback_manager_; |
+ |
// Current width and height of the offscreen frame buffer. |
gfx::Size offscreen_size_; |
@@ -2749,6 +2792,11 @@ bool GLES2DecoderImpl::Initialize(const scoped_refptr<gfx::GLSurface>& surface, |
return false; |
} |
CHECK_GL_ERROR(); |
+ |
+ bool needs_emulation = feature_info_->gl_version_info().IsLowerThanGL(4, 2); |
+ transform_feedback_manager_.reset(new TransformFeedbackManager( |
+ group_->max_transform_feedback_separate_attribs(), needs_emulation)); |
+ |
if (feature_info_->context_type() == CONTEXT_TYPE_WEBGL2 || |
feature_info_->context_type() == CONTEXT_TYPE_OPENGLES3) { |
if (!feature_info_->IsES3Capable()) { |
@@ -2762,6 +2810,15 @@ bool GLES2DecoderImpl::Initialize(const scoped_refptr<gfx::GLSurface>& surface, |
frag_depth_explicitly_enabled_ = true; |
draw_buffers_explicitly_enabled_ = true; |
// TODO(zmo): Look into shader_texture_lod_explicitly_enabled_ situation. |
+ |
+ // Create a fake default transform feedback and bind to it. |
+ GLuint default_transform_feedback = 0; |
+ glGenTransformFeedbacks(1, &default_transform_feedback); |
+ state_.default_transform_feedback = |
+ transform_feedback_manager_->CreateTransformFeedback( |
+ 0, default_transform_feedback); |
+ glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, default_transform_feedback); |
+ state_.bound_transform_feedback = state_.default_transform_feedback.get(); |
} |
state_.attrib_values.resize(group_->max_vertex_attribs()); |
@@ -3505,6 +3562,21 @@ bool GLES2DecoderImpl::GenSamplersHelper(GLsizei n, const GLuint* client_ids) { |
return true; |
} |
+bool GLES2DecoderImpl::GenTransformFeedbacksHelper( |
+ GLsizei n, const GLuint* client_ids) { |
+ for (GLsizei ii = 0; ii < n; ++ii) { |
+ if (GetTransformFeedback(client_ids[ii])) { |
+ return false; |
+ } |
+ } |
+ std::unique_ptr<GLuint[]> service_ids(new GLuint[n]); |
+ glGenTransformFeedbacks(n, service_ids.get()); |
+ for (GLsizei ii = 0; ii < n; ++ii) { |
+ CreateTransformFeedback(client_ids[ii], service_ids[ii]); |
+ } |
+ return true; |
+} |
+ |
bool GLES2DecoderImpl::GenPathsCHROMIUMHelper(GLuint first_client_id, |
GLsizei range) { |
GLuint last_client_id; |
@@ -3548,6 +3620,7 @@ void GLES2DecoderImpl::DeleteBuffersHelper( |
if (buffer && !buffer->IsDeleted()) { |
buffer->RemoveMappedRange(); |
state_.RemoveBoundBuffer(buffer); |
+ transform_feedback_manager_->RemoveBoundBuffer(buffer); |
RemoveBuffer(client_ids[ii]); |
} |
} |
@@ -3673,7 +3746,24 @@ void GLES2DecoderImpl::DeleteSamplersHelper( |
} |
} |
-// } // anonymous namespace |
+void GLES2DecoderImpl::DeleteTransformFeedbacksHelper( |
+ GLsizei n, const GLuint* client_ids) { |
+ for (GLsizei ii = 0; ii < n; ++ii) { |
+ TransformFeedback* transform_feedback = GetTransformFeedback( |
+ client_ids[ii]); |
+ if (transform_feedback) { |
+ if (state_.bound_transform_feedback.get() == transform_feedback) { |
+ // Bind to the default transform feedback. |
+ DCHECK(state_.default_transform_feedback.get()); |
+ state_.default_transform_feedback->DoBindTransformFeedback( |
+ GL_TRANSFORM_FEEDBACK); |
+ state_.bound_transform_feedback = |
+ state_.default_transform_feedback.get(); |
+ } |
+ RemoveTransformFeedback(client_ids[ii]); |
+ } |
+ } |
+} |
bool GLES2DecoderImpl::MakeCurrent() { |
if (!context_.get()) |
@@ -4075,23 +4165,25 @@ void GLES2DecoderImpl::Destroy(bool have_context) { |
if (!initialized()) |
return; |
- DCHECK(!have_context || context_->IsCurrent(NULL)); |
+ DCHECK(!have_context || context_->IsCurrent(nullptr)); |
// Unbind everything. |
- state_.vertex_attrib_manager = NULL; |
- state_.default_vertex_attrib_manager = NULL; |
+ state_.vertex_attrib_manager = nullptr; |
+ state_.default_vertex_attrib_manager = nullptr; |
state_.texture_units.clear(); |
state_.sampler_units.clear(); |
- state_.bound_array_buffer = NULL; |
- state_.bound_copy_read_buffer = NULL; |
- state_.bound_copy_write_buffer = NULL; |
- state_.bound_pixel_pack_buffer = NULL; |
- state_.bound_pixel_unpack_buffer = NULL; |
- state_.bound_transform_feedback_buffer = NULL; |
- state_.bound_uniform_buffer = NULL; |
- framebuffer_state_.bound_read_framebuffer = NULL; |
- framebuffer_state_.bound_draw_framebuffer = NULL; |
- state_.bound_renderbuffer = NULL; |
+ state_.bound_array_buffer = nullptr; |
+ state_.bound_copy_read_buffer = nullptr; |
+ state_.bound_copy_write_buffer = nullptr; |
+ state_.bound_pixel_pack_buffer = nullptr; |
+ state_.bound_pixel_unpack_buffer = nullptr; |
+ state_.bound_transform_feedback_buffer = nullptr; |
+ state_.bound_uniform_buffer = nullptr; |
+ framebuffer_state_.bound_read_framebuffer = nullptr; |
+ framebuffer_state_.bound_draw_framebuffer = nullptr; |
+ state_.bound_renderbuffer = nullptr; |
+ state_.bound_transform_feedback = nullptr; |
+ state_.default_transform_feedback = nullptr; |
if (offscreen_saved_color_texture_info_.get()) { |
DCHECK(offscreen_target_color_texture_); |
@@ -4186,6 +4278,14 @@ void GLES2DecoderImpl::Destroy(bool have_context) { |
vertex_array_manager_.reset(); |
} |
+ if (transform_feedback_manager_.get()) { |
+ if (!have_context) { |
+ transform_feedback_manager_->MarkContextLost(); |
+ } |
+ transform_feedback_manager_->Destroy(); |
+ transform_feedback_manager_.reset(); |
+ } |
+ |
if (image_manager_.get()) { |
image_manager_->Destroy(have_context); |
image_manager_.reset(); |
@@ -4714,7 +4814,12 @@ void GLES2DecoderImpl::BindIndexedBufferImpl( |
"index out of range"); |
return; |
} |
- // TODO(zmo): Check transform feedback isn't currently active. |
+ DCHECK(state_.bound_transform_feedback.get()); |
+ if (state_.bound_transform_feedback->active()) { |
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
+ "bound transform feedback is active"); |
+ return; |
+ } |
break; |
} |
case GL_UNIFORM_BUFFER: { |
@@ -4785,17 +4890,36 @@ void GLES2DecoderImpl::BindIndexedBufferImpl( |
} |
LogClientServiceForInfo(buffer, client_id, function_name); |
- switch (function_type) { |
- case kBindBufferBase: |
- glBindBufferBase(target, index, service_id); |
+ switch (target) { |
+ case GL_TRANSFORM_FEEDBACK_BUFFER: |
+ DCHECK(state_.bound_transform_feedback.get()); |
+ switch (function_type) { |
+ case kBindBufferBase: |
+ state_.bound_transform_feedback->DoBindBufferBase( |
+ target, index, buffer); |
+ break; |
+ case kBindBufferRange: |
+ state_.bound_transform_feedback->DoBindBufferRange( |
+ target, index, buffer, offset, size); |
+ break; |
+ } |
break; |
- case kBindBufferRange: |
- // TODO(zmo): On Desktop GL 4.1 or lower, clamp the offset/size not to |
- // exceed the size of the buffer. crbug.com/604436. |
- glBindBufferRange(target, index, service_id, offset, size); |
+ case GL_UNIFORM_BUFFER: |
+ // TODO(zmo): emulate Desktop GL 4.1 or lower behavior and keep track of |
+ // indexed buffer bindings. |
+ switch (function_type) { |
+ case kBindBufferBase: |
+ glBindBufferBase(target, index, service_id); |
+ break; |
+ case kBindBufferRange: |
+ glBindBufferRange(target, index, service_id, offset, size); |
+ break; |
+ } |
+ break; |
+ default: |
+ NOTREACHED(); |
break; |
} |
- // TODO(kbr): track indexed bound buffers. |
} |
void GLES2DecoderImpl::DoBindBufferBase(GLenum target, GLuint index, |
@@ -5121,6 +5245,89 @@ void GLES2DecoderImpl::DoBindSampler(GLuint unit, GLuint client_id) { |
state_.sampler_units[unit] = sampler; |
} |
+void GLES2DecoderImpl::DoBindTransformFeedback( |
+ GLenum target, GLuint client_id) { |
+ const char* function_name = "glBindTransformFeedback"; |
+ if (!validators_->transform_feedback_bind_target.IsValid(target)) { |
+ LOCAL_SET_GL_ERROR_INVALID_ENUM(function_name, target, "target"); |
+ return; |
+ } |
+ |
+ TransformFeedback* transform_feedback = nullptr; |
+ if (client_id != 0) { |
+ transform_feedback = GetTransformFeedback(client_id); |
+ if (!transform_feedback) { |
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
+ "id not generated by glGenTransformFeedbacks"); |
+ return; |
+ } |
+ } else { |
+ transform_feedback = state_.default_transform_feedback.get(); |
+ } |
+ DCHECK(transform_feedback); |
+ if (transform_feedback == state_.bound_transform_feedback.get()) |
+ return; |
+ if (state_.bound_transform_feedback->active() && |
+ !state_.bound_transform_feedback->paused()) { |
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
+ "currently bound transform feedback is active"); |
+ return; |
+ } |
+ LogClientServiceForInfo(transform_feedback, client_id, function_name); |
+ transform_feedback->DoBindTransformFeedback(target); |
+ state_.bound_transform_feedback = transform_feedback; |
+} |
+ |
+void GLES2DecoderImpl::DoBeginTransformFeedback(GLenum primitive_mode) { |
+ const char* function_name = "glBeginTransformFeedback"; |
+ DCHECK(state_.bound_transform_feedback.get()); |
+ if (!validators_->transform_feedback_primitive_mode.IsValid(primitive_mode)) { |
+ LOCAL_SET_GL_ERROR_INVALID_ENUM( |
+ function_name, primitive_mode, "primitiveMode"); |
+ return; |
+ } |
+ if (state_.bound_transform_feedback->active()) { |
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
+ "transform feedback is already active"); |
+ return; |
+ } |
+ state_.bound_transform_feedback->DoBeginTransformFeedback(primitive_mode); |
+} |
+ |
+void GLES2DecoderImpl::DoEndTransformFeedback() { |
+ const char* function_name = "glEndTransformFeedback"; |
+ DCHECK(state_.bound_transform_feedback.get()); |
+ if (!state_.bound_transform_feedback->active()) { |
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, |
+ "transform feedback is not active"); |
+ return; |
+ } |
+ // TODO(zmo): Validate binding points. |
+ state_.bound_transform_feedback->DoEndTransformFeedback(); |
+} |
+ |
+void GLES2DecoderImpl::DoPauseTransformFeedback() { |
+ DCHECK(state_.bound_transform_feedback.get()); |
+ if (!state_.bound_transform_feedback->active() || |
+ state_.bound_transform_feedback->paused()) { |
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glPauseTransformFeedback", |
+ "transform feedback is not active or already paused"); |
+ return; |
+ } |
+ state_.bound_transform_feedback->DoPauseTransformFeedback(); |
+} |
+ |
+void GLES2DecoderImpl::DoResumeTransformFeedback() { |
+ DCHECK(state_.bound_transform_feedback.get()); |
+ if (!state_.bound_transform_feedback->active() || |
+ !state_.bound_transform_feedback->paused()) { |
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glResumeTransformFeedback", |
+ "transform feedback is not active or not paused"); |
+ return; |
+ } |
+ state_.bound_transform_feedback->DoResumeTransformFeedback(); |
+} |
+ |
void GLES2DecoderImpl::DoDisableVertexAttribArray(GLuint index) { |
if (state_.vertex_attrib_manager->Enable(index, false)) { |
if (index != 0 || feature_info_->gl_version_info().BehavesLikeGLES()) { |
@@ -8824,6 +9031,12 @@ bool GLES2DecoderImpl::DoIsSampler(GLuint client_id) { |
return sampler && !sampler->IsDeleted(); |
} |
+bool GLES2DecoderImpl::DoIsTransformFeedback(GLuint client_id) { |
+ const TransformFeedback* transform_feedback = |
+ GetTransformFeedback(client_id); |
+ return transform_feedback && transform_feedback->has_been_bound(); |
+} |
+ |
void GLES2DecoderImpl::DoAttachShader( |
GLuint program_client_id, GLint shader_client_id) { |
Program* program = GetProgramInfoNotShader( |
@@ -13254,6 +13467,10 @@ void GLES2DecoderImpl::MarkContextLost(error::ContextLostReason reason) { |
context_lost_reason_ = reason; |
current_decoder_error_ = error::kLostContext; |
context_was_lost_ = true; |
+ |
+ if (transform_feedback_manager_.get()) { |
+ transform_feedback_manager_->MarkContextLost(); |
+ } |
} |
bool GLES2DecoderImpl::CheckResetStatus() { |