Chromium Code Reviews| Index: gpu/command_buffer/service/gles2_cmd_decoder.cc |
| =================================================================== |
| --- gpu/command_buffer/service/gles2_cmd_decoder.cc (revision 35070) |
| +++ gpu/command_buffer/service/gles2_cmd_decoder.cc (working copy) |
| @@ -18,28 +18,60 @@ |
| namespace gpu { |
| namespace gles2 { |
| +// Check that certain assumptions the code makes are true. There are places in |
| +// the code where shared memory is passed direclty to GL. Example, glUniformiv, |
| +// glShaderSource. The command buffer code assumes GLint and GLsizei (and maybe |
| +// a few others) are 32bits. If they are not 32bits the code will have to change |
| +// to call those GL functions with service side memory and then copy the results |
| +// to shared memory, converting the sizes. |
| +COMPILE_ASSERT(sizeof(GLint) == sizeof(uint32), // NOLINT |
| + GLint_not_same_size_as_uint32); |
| +COMPILE_ASSERT(sizeof(GLsizei) == sizeof(uint32), // NOLINT |
| + GLint_not_same_size_as_uint32); |
| + |
| namespace { |
| +size_t GetGLTypeSize(GLenum type) { |
| + switch (type) { |
| + case GL_BYTE: |
| + return sizeof(GLbyte); // NOLINT |
| + case GL_UNSIGNED_BYTE: |
| + return sizeof(GLubyte); // NOLINT |
| + case GL_SHORT: |
| + return sizeof(GLshort); // NOLINT |
| + case GL_UNSIGNED_SHORT: |
| + return sizeof(GLushort); // NOLINT |
| + case GL_FLOAT: |
| + return sizeof(GLfloat); // NOLINT |
| + default: |
| + return 0; |
| + } |
| +} |
| + |
| // Returns the address of the first byte after a struct. |
| template <typename T> |
| const void* AddressAfterStruct(const T& pod) { |
| return reinterpret_cast<const uint8*>(&pod) + sizeof(pod); |
| } |
| -// Returns the address of the frst byte after the struct. |
| +// Returns the address of the frst byte after the struct or NULL if size > |
| +// immediate_data_size. |
| template <typename RETURN_TYPE, typename COMMAND_TYPE> |
| -RETURN_TYPE GetImmediateDataAs(const COMMAND_TYPE& pod) { |
| - return static_cast<RETURN_TYPE>(const_cast<void*>(AddressAfterStruct(pod))); |
| +RETURN_TYPE GetImmediateDataAs(const COMMAND_TYPE& pod, |
| + uint32 size, |
| + uint32 immediate_data_size) { |
| + return (size <= immediate_data_size) ? |
| + static_cast<RETURN_TYPE>(const_cast<void*>(AddressAfterStruct(pod))) : |
| + NULL; |
| } |
| -// Checks if there is enough immediate data. |
| -template<typename T> |
| -bool CheckImmediateDataSize( |
| +// Computes the data size for certain gl commands like glUniform. |
| +uint32 ComputeImmediateDataSize( |
| uint32 immediate_data_size, |
| GLuint count, |
| size_t size, |
| unsigned int elements_per_unit) { |
| - return immediate_data_size == count * size * elements_per_unit; |
| + return count * size * elements_per_unit; |
| } |
| // A struct to hold info about each command. |
| @@ -59,39 +91,6 @@ |
| #undef GLES2_CMD_OP |
| }; |
| -// These commands convert from c calls to local os calls. |
| -void GLGenBuffersHelper(GLsizei n, GLuint* ids) { |
| - glGenBuffersARB(n, ids); |
| -} |
| - |
| -void GLGenFramebuffersHelper(GLsizei n, GLuint* ids) { |
| - glGenFramebuffersEXT(n, ids); |
| -} |
| - |
| -void GLGenRenderbuffersHelper(GLsizei n, GLuint* ids) { |
| - glGenRenderbuffersEXT(n, ids); |
| -} |
| - |
| -void GLGenTexturesHelper(GLsizei n, GLuint* ids) { |
| - glGenTextures(n, ids); |
| -} |
| - |
| -void GLDeleteBuffersHelper(GLsizei n, GLuint* ids) { |
| - glDeleteBuffersARB(n, ids); |
| -} |
| - |
| -void GLDeleteFramebuffersHelper(GLsizei n, GLuint* ids) { |
| - glDeleteFramebuffersEXT(n, ids); |
| -} |
| - |
| -void GLDeleteRenderbuffersHelper(GLsizei n, GLuint* ids) { |
| - glDeleteRenderbuffersEXT(n, ids); |
| -} |
| - |
| -void GLDeleteTexturesHelper(GLsizei n, GLuint* ids) { |
| - glDeleteTextures(n, ids); |
| -} |
| - |
| namespace GLErrorBit { |
| enum GLErrorBit { |
| kNoError = 0, |
| @@ -221,6 +220,131 @@ |
| // cmd stuff to outside this class. |
| class GLES2DecoderImpl : public GLES2Decoder { |
| public: |
| + // Info about Vertex Attributes. This is used to track what the user currently |
| + // has bound on each Vertex Attribute so that checking can be done at |
| + // glDrawXXX time. |
| + class VertexAttribInfo { |
| + public: |
| + VertexAttribInfo() |
| + : enabled_(false), |
| + size_(0), |
| + type_(0), |
| + offset_(0), |
| + real_stride_(0), |
| + buffer_(0), |
| + buffer_size_(0), |
| + num_elements_(0) { |
| + } |
| + // Returns true if this VertexAttrib can access index. |
| + bool CanAccess(GLuint index); |
| + |
| + void set_enabled(bool enabled) { |
| + enabled_ = enabled; |
| + } |
| + |
| + GLuint buffer() const { |
| + return buffer_; |
| + } |
| + |
| + void Clear() { |
| + buffer_ = 0; |
| + SetBufferSize(0); |
| + } |
| + |
| + void SetBufferSize(GLsizeiptr buffer_size) { |
| + buffer_size_ = buffer_size; |
| + if (offset_ > buffer_size || real_stride_ == 0) { |
| + num_elements_ = 0; |
| + } else { |
| + uint32 size = buffer_size - offset_; |
| + num_elements_ = size / real_stride_ + |
| + (size % real_stride_ >= GetGLTypeSize(type_) ? 1 : 0); |
| + } |
| + } |
| + |
| + void SetInfo( |
| + GLuint buffer, |
| + GLsizeiptr buffer_size, |
| + GLint size, |
| + GLenum type, |
| + GLsizei real_stride, |
| + GLsizei offset) { |
| + DCHECK(real_stride > 0); |
| + buffer_ = buffer; |
| + size_ = size; |
| + type_ = type; |
| + real_stride_ = real_stride; |
| + offset_ = offset; |
| + SetBufferSize(buffer_size); |
| + } |
| + |
| + private: |
| + // Whether or not this attribute is enabled. |
| + bool enabled_; |
| + |
| + // number of components (1, 2, 3, 4) |
| + GLint size_; |
| + |
| + // GL_BYTE, GL_FLOAT, etc. See glVertexAttribPointer. |
| + GLenum type_; |
| + |
| + // The offset into the buffer. |
| + GLsizei offset_; |
| + |
| + // The stride that will be used to access the buffer. This is the actual |
| + // stide, NOT the GL bogus stride. In other words there is never a stride |
| + // of 0. |
| + GLsizei real_stride_; |
| + |
| + // The service side name of the buffer bound to this attribute. 0 = invalid |
| + GLuint buffer_; |
| + |
| + // The size of the buffer. |
| + GLsizeiptr buffer_size_; |
| + |
| + // The number of elements that can be accessed. |
| + GLuint num_elements_; |
| + }; |
| + |
| + // Info about Buffers currently in the system. |
| + struct BufferInfo { |
| + BufferInfo() |
| + : size(0) { |
| + } |
| + |
| + explicit BufferInfo(GLsizeiptr _size) |
| + : size(_size) { |
| + } |
| + |
| + GLsizeiptr size; |
| + }; |
| + |
| + // This is used to track which attributes a particular program needs |
| + // so we can verify at glDrawXXX time that every attribute is either disabled |
| + // or if enabled that it points to a valid source. |
| + class ProgramInfo { |
| + public: |
| + typedef std::vector<GLuint> AttribLocationVector; |
| + |
| + ProgramInfo() { |
| + } |
| + |
| + void SetNumAttributes(int num_attribs) { |
| + attrib_locations_.resize(num_attribs); |
| + } |
| + |
| + void SetAttributeLocation(GLuint index, int location) { |
| + DCHECK(index < attrib_locations_.size()); |
| + attrib_locations_[index] = location; |
| + } |
| + |
| + const AttribLocationVector& GetAttribLocations() const { |
| + return attrib_locations_; |
| + } |
| + private: |
| + AttribLocationVector attrib_locations_; |
| + }; |
| + |
| GLES2DecoderImpl(); |
| // Overridden from AsyncAPIInterface. |
| @@ -237,41 +361,59 @@ |
| // Overridden from GLES2Decoder. |
| virtual void Destroy(); |
| + // Removes any buffers in the VertexAtrribInfos and BufferInfos. This is used |
| + // on glDeleteBuffers so we can make sure the user does not try to render |
| + // with deleted buffers. |
| + void RemoveBufferInfo(GLuint buffer_id); |
| + |
| private: |
| bool InitPlatformSpecific(); |
| bool InitGlew(); |
| // Template to help call glGenXXX functions. |
| - template <void gl_gen_function(GLsizei, GLuint*)> |
| + template <void gl_gen_function(GLES2DecoderImpl*, GLsizei, GLuint*)> |
| bool GenGLObjects(GLsizei n, const GLuint* client_ids) { |
| // TODO(gman): Verify client ids are unused. |
| scoped_array<GLuint>temp(new GLuint[n]); |
| - gl_gen_function(n, temp.get()); |
| + gl_gen_function(this, n, temp.get()); |
| // TODO(gman): check for success before copying results. |
| - for (GLsizei ii = 0; ii < n; ++ii) { |
| - if (!id_map_.AddMapping(client_ids[ii], temp[ii])) { |
| - // TODO(gman): fail. |
| - } |
| - } |
| - return true; |
| + return RegisterObjects(n, client_ids, temp.get()); |
| } |
| // Template to help call glDeleteXXX functions. |
| - template <void gl_delete_function(GLsizei, GLuint*)> |
| + template <void gl_delete_function(GLES2DecoderImpl*, GLsizei, GLuint*)> |
| bool DeleteGLObjects(GLsizei n, const GLuint* client_ids) { |
| scoped_array<GLuint>temp(new GLuint[n]); |
| - // TODO(gman): check for success before copying results. |
| - for (GLsizei ii = 0; ii < n; ++ii) { |
| - if (id_map_.GetServiceId(client_ids[ii], &temp[ii])) { |
| - id_map_.RemoveMapping(client_ids[ii], temp[ii]); |
| - } else { |
| - temp[ii] = 0; |
| - } |
| - } |
| - gl_delete_function(n, temp.get()); |
| + UnregisterObjects(n, client_ids, temp.get()); |
| + gl_delete_function(this, n, temp.get()); |
| return true; |
| } |
| + // Register client ids with generated service ids. |
| + bool RegisterObjects( |
| + GLsizei n, const GLuint* client_ids, const GLuint* service_ids); |
| + |
| + // Unregisters client ids with service ids. |
| + void UnregisterObjects( |
| + GLsizei n, const GLuint* client_ids, GLuint* service_ids); |
| + |
| + // Gets the program info for the given program. Returns NULL if none exists. |
| + // Programs that have no had glLinkProgram succesfully called on them will |
| + // not exist. |
| + ProgramInfo* GetProgramInfo(GLuint program); |
| + |
| + // Updates the program info for the given program. |
| + void UpdateProgramInfo(GLuint program); |
| + |
| + // Deletes the program info for the given program. |
| + void RemoveProgramInfo(GLuint program); |
| + |
| + // Gets the buffer info for the given buffer. |
| + const BufferInfo* GetBufferInfo(GLuint buffer); |
| + |
| + // Sets the info for a buffer. |
| + void SetBufferInfo(GLuint buffer, GLsizeiptr size); |
| + |
| // Wrapper for glCreateProgram |
| void CreateProgramHelper(GLuint client_id); |
| @@ -281,15 +423,46 @@ |
| // Wrapper for glBindBuffer since we need to track the current targets. |
| void DoBindBuffer(GLenum target, GLuint buffer); |
| + // Wrapper for glBindBuffer since we need to track the current targets. |
|
apatrick
2009/12/22 02:37:21
BindBuffer -> DrawArrays
|
| + void DoDrawArrays(GLenum mode, GLint first, GLsizei count); |
| + |
| + // Wrapper for glDisableVertexAttribArray. |
| + void DoDisableVertexAttribArray(GLuint index); |
| + |
| + // Wrapper for glEnableVertexAttribArray. |
| + void DoEnableVertexAttribArray(GLuint index); |
| + |
| + // Wrapper for glLinkProgram |
| + void DoLinkProgram(GLuint program); |
| + |
| // Swaps the buffers (copies/renders to the current window). |
| void DoSwapBuffers(); |
| + // Wrapper for glUseProgram |
| + void DoUseProgram(GLuint program); |
| + |
| // Gets the GLError through our wrapper. |
| GLenum GetGLError(); |
| // Sets our wrapper for the GLError. |
| void SetGLError(GLenum error); |
| + // Copies the real GL errors to the wrapper. This is so we can |
| + // make sure there are no native GL errors before calling some GL function |
| + // so that on return we know any error generated was for that specific |
| + // command. |
| + void CopyRealGLErrorsToWrapper(); |
| + |
| + // Checks if the current program and vertex attributes are valid for drawing. |
| + bool IsDrawValid(GLuint max_vertex_accessed); |
| + |
| + // Gets the buffer id for a given target. |
| + GLuint GetBufferForTarget(GLenum target) { |
| + DCHECK(target == GL_ARRAY_BUFFER || target == GL_ELEMENT_ARRAY_BUFFER); |
| + return target == GL_ARRAY_BUFFER ? bound_array_buffer_ : |
| + bound_element_array_buffer_; |
| + } |
| + |
| // Generate a member function prototype for each command in an automated and |
| // typesafe way. |
| #define GLES2_CMD_OP(name) \ |
| @@ -324,6 +497,26 @@ |
| // to call glDrawElements. |
| GLuint bound_element_array_buffer_; |
| + // The maximum vertex attributes. |
| + GLuint max_vertex_attribs_; |
| + |
| + // Info for each vertex attribute saved so we can check at glDrawXXX time |
| + // if it is safe to draw. |
| + scoped_array<VertexAttribInfo> vertex_attrib_infos_; |
| + |
| + // Infor for each buffer in the system. |
|
apatrick
2009/12/22 02:37:21
Infor -> Info
|
| + // TODO(gman): Choose a faster container. |
| + typedef std::map<GLuint, BufferInfo> BufferInfoMap; |
| + BufferInfoMap buffer_infos_; |
| + |
| + // Info for each "successfully linked" program by service side program Id. |
| + // TODO(gman): Choose a faster container. |
| + typedef std::map<GLuint, ProgramInfo> ProgramInfoMap; |
| + ProgramInfoMap program_infos_; |
| + |
| + // The program in current use through glUseProgram. |
| + ProgramInfo* current_program_info_; |
| + |
| #if defined(OS_WIN) |
| HDC device_context_; |
| HGLRC gl_context_; |
| @@ -346,6 +539,8 @@ |
| unpack_alignment_(4), |
| bound_array_buffer_(0), |
| bound_element_array_buffer_(0), |
| + max_vertex_attribs_(0), |
| + current_program_info_(NULL), |
| #ifdef OS_WIN |
| device_context_(NULL), |
| gl_context_(NULL), |
| @@ -360,6 +555,17 @@ |
| return false; |
| CHECK_GL_ERROR(); |
| + // Lookup GL things we need to know. |
| + GLint value; |
| + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &value); |
| + max_vertex_attribs_ = value; |
| + |
| + DCHECK_GE(max_vertex_attribs_, 8u); |
| + |
| + vertex_attrib_infos_.reset(new VertexAttribInfo[max_vertex_attribs_]); |
| + memset(vertex_attrib_infos_.get(), 0, |
| + sizeof(vertex_attrib_infos_[0]) * max_vertex_attribs_); |
| + |
| //glBindFramebuffer(0, 0); |
| return true; |
| } |
| @@ -533,9 +739,84 @@ |
| return true; |
| } |
| +// These commands convert from c calls to local os calls. |
| +void GLGenBuffersHelper( |
| + GLES2DecoderImpl*, GLsizei n, GLuint* ids) { |
| + glGenBuffersARB(n, ids); |
| +} |
| + |
| +void GLGenFramebuffersHelper( |
| + GLES2DecoderImpl*, GLsizei n, GLuint* ids) { |
| + glGenFramebuffersEXT(n, ids); |
| +} |
| + |
| +void GLGenRenderbuffersHelper( |
| + GLES2DecoderImpl*, GLsizei n, GLuint* ids) { |
| + glGenRenderbuffersEXT(n, ids); |
| +} |
| + |
| +void GLGenTexturesHelper( |
| + GLES2DecoderImpl*, GLsizei n, GLuint* ids) { |
| + glGenTextures(n, ids); |
| +} |
| + |
| +void GLDeleteBuffersHelper( |
| + GLES2DecoderImpl* decoder, GLsizei n, GLuint* ids) { |
| + glDeleteBuffersARB(n, ids); |
| + for (GLsizei ii = 0; ii < n; ++ii) { |
| + decoder->RemoveBufferInfo(ids[ii]); |
| + } |
| +} |
| + |
| +void GLDeleteFramebuffersHelper( |
| + GLES2DecoderImpl*, GLsizei n, GLuint* ids) { |
| + glDeleteFramebuffersEXT(n, ids); |
| +} |
| + |
| +void GLDeleteRenderbuffersHelper( |
| + GLES2DecoderImpl*, GLsizei n, GLuint* ids) { |
| + glDeleteRenderbuffersEXT(n, ids); |
| +} |
| + |
| +void GLDeleteTexturesHelper( |
| + GLES2DecoderImpl*, GLsizei n, GLuint* ids) { |
| + glDeleteTextures(n, ids); |
| +} |
| + |
| } // anonymous namespace |
| #endif |
| +bool GLES2DecoderImpl::RegisterObjects( |
| + GLsizei n, const GLuint* client_ids, const GLuint* service_ids) { |
| + for (GLsizei ii = 0; ii < n; ++ii) { |
| + if (!id_map_.AddMapping(client_ids[ii], service_ids[ii])) { |
| + // TODO(gman): fail. |
| + } |
| + } |
| + return true; |
| +} |
| + |
| +void GLES2DecoderImpl::UnregisterObjects( |
| + GLsizei n, const GLuint* client_ids, GLuint* service_ids) { |
| + // TODO(gman): check for success before copying results. |
| + for (GLsizei ii = 0; ii < n; ++ii) { |
| + if (id_map_.GetServiceId(client_ids[ii], &service_ids[ii])) { |
| + id_map_.RemoveMapping(client_ids[ii], service_ids[ii]); |
| + } else { |
| + service_ids[ii] = 0; |
| + } |
| + } |
| +} |
| + |
| +void GLES2DecoderImpl::RemoveBufferInfo(GLuint buffer_id) { |
| + for (GLuint ii = 0; ii < max_vertex_attribs_; ++ii) { |
| + if (vertex_attrib_infos_[ii].buffer() == buffer_id) { |
| + vertex_attrib_infos_[ii].Clear(); |
| + } |
| + } |
| + buffer_infos_.erase(buffer_id); |
| +} |
| + |
| bool GLES2DecoderImpl::InitPlatformSpecific() { |
| #if defined(OS_WIN) |
| device_context_ = ::GetDC(hwnd()); |
| @@ -678,8 +959,10 @@ |
| #undef GLES2_CMD_OP |
| } |
| if (debug()) { |
| - if (glGetError() != 0) { |
| + GLenum error; |
| + while ((error = glGetError()) != GL_NO_ERROR) { |
| // TODO(gman): Change output to something useful for NaCl. |
| + SetGLError(error); |
| printf("GL ERROR b4: %s\n", GetCommandName(command)); |
| } |
| } |
| @@ -723,6 +1006,24 @@ |
| glBindBuffer(target, buffer); |
| } |
| +void GLES2DecoderImpl::DoDisableVertexAttribArray(GLuint index) { |
| + if (index < max_vertex_attribs_) { |
| + vertex_attrib_infos_[index].set_enabled(false); |
| + glEnableVertexAttribArray(index); |
| + } else { |
| + SetGLError(GL_INVALID_VALUE); |
| + } |
| +} |
| + |
| +void GLES2DecoderImpl::DoEnableVertexAttribArray(GLuint index) { |
| + if (index < max_vertex_attribs_) { |
| + vertex_attrib_infos_[index].set_enabled(true); |
| + glEnableVertexAttribArray(index); |
| + } else { |
| + SetGLError(GL_INVALID_VALUE); |
| + } |
| +} |
| + |
| parse_error::ParseError GLES2DecoderImpl::HandleDeleteShader( |
| uint32 immediate_data_size, const gles2::DeleteShader& c) { |
| GLuint shader = c.shader; |
| @@ -731,7 +1032,7 @@ |
| SetGLError(GL_INVALID_VALUE); |
| return parse_error::kParseNoError; |
| } |
| - glDeleteProgram(service_id); |
| + glDeleteShader(service_id); |
| id_map_.RemoveMapping(shader, service_id); |
| return parse_error::kParseNoError; |
| } |
| @@ -744,11 +1045,31 @@ |
| SetGLError(GL_INVALID_VALUE); |
| return parse_error::kParseNoError; |
| } |
| + RemoveProgramInfo(program); |
| glDeleteProgram(service_id); |
| id_map_.RemoveMapping(program, service_id); |
| return parse_error::kParseNoError; |
| } |
| +void GLES2DecoderImpl::DoDrawArrays( |
| + GLenum mode, GLint first, GLsizei count) { |
| + if (IsDrawValid(first + count - 1)) { |
| + glDrawArrays(mode, first, count); |
| + } |
| +} |
| + |
| +void GLES2DecoderImpl::DoLinkProgram(GLuint program) { |
| + CopyRealGLErrorsToWrapper(); |
| + glLinkProgram(program); |
| + GLenum error = glGetError(); |
| + if (error != GL_NO_ERROR) { |
| + RemoveProgramInfo(program); |
| + SetGLError(error); |
| + } else { |
| + UpdateProgramInfo(program); |
| + } |
| +}; |
| + |
| // NOTE: If you need to know the results of SwapBuffers (like losing |
| // the context) then add a new command. Do NOT make SwapBuffers synchronous. |
| void GLES2DecoderImpl::DoSwapBuffers() { |
| @@ -762,6 +1083,17 @@ |
| #endif |
| } |
| +void GLES2DecoderImpl::DoUseProgram(GLuint program) { |
| + ProgramInfo* info = GetProgramInfo(program); |
| + if (!info) { |
| + // Program was not linked successfully. (ie, glLinkProgram) |
| + SetGLError(GL_INVALID_OPERATION); |
| + } else { |
| + current_program_info_ = info; |
| + glUseProgram(program); |
| + } |
| +} |
| + |
| GLenum GLES2DecoderImpl::GetGLError() { |
| // Check the GL error first, then our wrapped error. |
| GLenum error = glGetError(); |
| @@ -786,6 +1118,97 @@ |
| error_bits_ |= GLErrorToErrorBit(error); |
| } |
| +void GLES2DecoderImpl::CopyRealGLErrorsToWrapper() { |
| + GLenum error; |
| + while ((error = glGetError()) != GL_NO_ERROR) { |
| + SetGLError(error); |
| + } |
| +} |
| + |
| +const GLES2DecoderImpl::BufferInfo* GLES2DecoderImpl::GetBufferInfo( |
| + GLuint buffer) { |
| + BufferInfoMap::iterator it = buffer_infos_.find(buffer); |
| + return it != buffer_infos_.end() ? &it->second : NULL; |
| +} |
| + |
| +void GLES2DecoderImpl::SetBufferInfo(GLuint buffer, GLsizeiptr size) { |
| + buffer_infos_[buffer] = BufferInfo(size); |
| + |
| + // Also go through VertexAttribInfo and update any info that references |
| + // the same buffer. |
| + for (GLuint ii = 0; ii < max_vertex_attribs_; ++ii) { |
| + if (vertex_attrib_infos_[ii].buffer() == buffer) { |
| + vertex_attrib_infos_[ii].SetBufferSize(size); |
| + } |
| + } |
| +} |
| + |
| +GLES2DecoderImpl::ProgramInfo* GLES2DecoderImpl::GetProgramInfo( |
| + GLuint program) { |
| + ProgramInfoMap::iterator it = program_infos_.find(program); |
| + return it != program_infos_.end() ? &it->second : NULL; |
| +} |
| + |
| +void GLES2DecoderImpl::UpdateProgramInfo(GLuint program) { |
| + ProgramInfo* info = GetProgramInfo(program); |
| + if (!info) { |
| + std::pair<ProgramInfoMap::iterator, bool> result = |
| + program_infos_.insert(std::make_pair(program, ProgramInfo())); |
| + DCHECK(result.second); |
| + info = &result.first->second; |
| + } |
| + GLint num_attribs = 0; |
| + GLint max_len = 0; |
| + glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &num_attribs); |
| + info->SetNumAttributes(num_attribs); |
| + glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_len); |
| + // TODO(gman): Should we check for error? |
| + scoped_array<char> name_buffer(new char[max_len + 1]); |
| + for (GLint ii = 0; ii < num_attribs; ++ii) { |
| + GLsizei length; |
| + GLsizei size; |
| + GLenum type; |
| + glGetActiveAttrib( |
| + program, ii, max_len + 1, &length, &size, &type, name_buffer.get()); |
| + // TODO(gman): Should we check for error? |
| + GLint location = glGetAttribLocation(program, name_buffer.get()); |
| + info->SetAttributeLocation(ii, num_attribs); |
| + } |
| +} |
| + |
| +void GLES2DecoderImpl::RemoveProgramInfo(GLuint program) { |
| + ProgramInfoMap::iterator it = program_infos_.find(program); |
| + if (it != program_infos_.end()) { |
| + if (current_program_info_ == &it->second) { |
| + current_program_info_ = NULL; |
| + } |
| + program_infos_.erase(it); |
| + } |
| +} |
| + |
| +bool GLES2DecoderImpl::VertexAttribInfo::CanAccess(GLuint index) { |
| + return !enabled_ || (buffer_ != 0 && index < num_elements_); |
| +} |
| + |
| +bool GLES2DecoderImpl::IsDrawValid(GLuint max_vertex_accessed) { |
| + if (current_program_info_) { |
| + // Validate that all attribs current program needs are setup correctly. |
| + const ProgramInfo::AttribLocationVector& locations = |
| + current_program_info_->GetAttribLocations(); |
| + for (size_t ii = 0; ii < locations.size(); ++ii) { |
| + GLuint location = locations[ii]; |
| + DCHECK_LT(location, max_vertex_attribs_); |
| + if (!vertex_attrib_infos_[location].CanAccess(max_vertex_accessed)) { |
| + SetGLError(GL_INVALID_OPERATION); |
| + } |
| + } |
| + return true; |
| + } |
| + // We do not set a GL error here because the GL spec says no error if the |
| + // program is invalid. |
| + return false; |
| +}; |
| + |
| parse_error::ParseError GLES2DecoderImpl::HandleDrawElements( |
| uint32 immediate_data_size, const gles2::DrawElements& c) { |
| if (bound_element_array_buffer_ != 0) { |
| @@ -797,9 +1220,15 @@ |
| SetGLError(GL_INVALID_VALUE); |
| } else { |
| const GLvoid* indices = reinterpret_cast<const GLvoid*>(c.index_offset); |
| - // TODO(gman): Validate indices |
| - // TOOD(gman): Validate all attribs current program needs are setup. |
| - glDrawElements(mode, count, type, indices); |
| + // TODO(gman): Validate indices. Get maximum index. |
| + // |
| + // This value should be computed by walking the index buffer from 0 to |
| + // count and finding the maximum vertex accessed. |
| + // For now we'll special case 0 to not check. |
| + GLuint max_vertex_accessed = 0; |
| + if (IsDrawValid(max_vertex_accessed)) { |
| + glDrawElements(mode, count, type, indices); |
| + } |
| } |
| } else { |
| SetGLError(GL_INVALID_VALUE); |
| @@ -865,8 +1294,8 @@ |
| } |
| GLsizei count = c.count; |
| uint32 data_size = c.data_size; |
| - // TODO(gman): need to check that data_size is in range for arg_count. |
| - const char** data = GetImmediateDataAs<const char**>(c); |
| + const char** data = GetImmediateDataAs<const char**>( |
| + c, data_size, immediate_data_size); |
| if (!data) { |
| return parse_error::kParseOutOfBounds; |
| } |
| @@ -876,21 +1305,35 @@ |
| parse_error::ParseError GLES2DecoderImpl::HandleVertexAttribPointer( |
| uint32 immediate_data_size, const gles2::VertexAttribPointer& c) { |
| - // TODO(gman): Is this a valid check or does this check have to come |
| - // at glDrawElements time. |
| if (bound_array_buffer_ != 0) { |
| GLuint indx = c.indx; |
| GLint size = c.size; |
| GLenum type = c.type; |
| GLboolean normalized = c.normalized; |
| GLsizei stride = c.stride; |
| - GLuint offset = c.offset; |
| - const void* ptr = reinterpret_cast<const void*>(c.offset); |
| + GLsizei offset = c.offset; |
| + const void* ptr = reinterpret_cast<const void*>(offset); |
| if (!ValidateGLenumVertexAttribType(type) || |
| - !ValidateGLenumVertexAttribSize(size)) { |
| + !ValidateGLenumVertexAttribSize(size) || |
| + indx >= max_vertex_attribs_ || |
| + stride < 0) { |
| SetGLError(GL_INVALID_VALUE); |
| return parse_error::kParseNoError; |
| } |
| + const BufferInfo* buffer_info = GetBufferInfo(bound_array_buffer_); |
| + GLsizei component_size = GetGLTypeSize(type); |
| + GLsizei real_stride = stride != 0 ? stride : component_size * size; |
| + if (offset % component_size > 0) { |
| + SetGLError(GL_INVALID_VALUE); |
| + return parse_error::kParseNoError; |
| + } |
| + vertex_attrib_infos_[indx].SetInfo( |
| + bound_array_buffer_, |
| + buffer_info ? buffer_info->size : 0, |
| + size, |
| + type, |
| + real_stride, |
| + offset); |
| glVertexAttribPointer(indx, size, type, normalized, stride, ptr); |
| } else { |
| SetGLError(GL_INVALID_VALUE); |
| @@ -975,9 +1418,8 @@ |
| return parse_error::kParseNoError; |
| } |
| uint32 name_size = c.data_size; |
| - const char* name = GetImmediateDataAs<const char*>(c); |
| - // TODO(gman): Make sure validate checks arg_count |
| - // covers data_size. |
| + const char* name = GetImmediateDataAs<const char*>( |
| + c, name_size, immediate_data_size); |
| GLint* location = GetSharedMemoryAs<GLint*>( |
| c.location_shm_id, c.location_shm_offset, sizeof(GLint)); |
| if (!location || !name) { |
| @@ -1016,9 +1458,8 @@ |
| return parse_error::kParseNoError; |
| } |
| uint32 name_size = c.data_size; |
| - const char* name = GetImmediateDataAs<const char*>(c); |
| - // TODO(gman): Make sure validate checks arg_count |
| - // covers data_size. |
| + const char* name = GetImmediateDataAs<const char*>( |
| + c, name_size, immediate_data_size); |
| GLint* location = GetSharedMemoryAs<GLint*>( |
| c.location_shm_id, c.location_shm_offset, sizeof(GLint)); |
| if (!location || !name) { |
| @@ -1043,13 +1484,26 @@ |
| return parse_error::kParseOutOfBounds; |
| } |
| } |
| - // TODO(gman): Validate case where data is NULL. |
| if (!ValidateGLenumBufferTarget(target) || |
| !ValidateGLenumBufferUsage(usage)) { |
| SetGLError(GL_INVALID_VALUE); |
| return parse_error::kParseNoError; |
| } |
| + // Clear the buffer to 0 if no initial data was passed in. |
| + scoped_array<int8> zero; |
| + if (!data) { |
| + zero.reset(new int8[size]); |
| + memset(zero.get(), 0, size); |
| + data = zero.get(); |
| + } |
| + CopyRealGLErrorsToWrapper(); |
| glBufferData(target, size, data, usage); |
| + GLenum error = glGetError(); |
| + if (error != GL_NO_ERROR) { |
| + SetGLError(error); |
| + } else { |
| + SetBufferInfo(GetBufferForTarget(target), size); |
| + } |
| return parse_error::kParseNoError; |
| } |
| @@ -1057,15 +1511,25 @@ |
| uint32 immediate_data_size, const gles2::BufferDataImmediate& c) { |
| GLenum target = static_cast<GLenum>(c.target); |
| GLsizeiptr size = static_cast<GLsizeiptr>(c.size); |
| - const void* data = GetImmediateDataAs<const void*>(c); |
| + const void* data = GetImmediateDataAs<const void*>( |
| + c, size, immediate_data_size); |
| + if (!data) { |
| + return parse_error::kParseOutOfBounds; |
| + } |
| GLenum usage = static_cast<GLenum>(c.usage); |
| if (!ValidateGLenumBufferTarget(target) || |
| !ValidateGLenumBufferUsage(usage)) { |
| SetGLError(GL_INVALID_VALUE); |
| return parse_error::kParseNoError; |
| } |
| - // TODO(gman): Handle case where data is NULL. |
| + CopyRealGLErrorsToWrapper(); |
| glBufferData(target, size, data, usage); |
| + GLenum error = glGetError(); |
| + if (error != GL_NO_ERROR) { |
| + SetGLError(error); |
| + } else { |
| + SetBufferInfo(GetBufferForTarget(target), size); |
| + } |
| return parse_error::kParseNoError; |
| } |
| @@ -1093,7 +1557,12 @@ |
| SetGLError(GL_INVALID_VALUE); |
| return parse_error::kParseNoError; |
| } |
| - // TODO(gman): Validate case where data is NULL. |
| + scoped_array<int8> zero; |
| + if (!data) { |
| + zero.reset(new int8[image_size]); |
| + memset(zero.get(), 0, image_size); |
| + data = zero.get(); |
| + } |
| glCompressedTexImage2D( |
| target, level, internal_format, width, height, border, image_size, data); |
| return parse_error::kParseNoError; |
| @@ -1108,9 +1577,8 @@ |
| GLsizei height = static_cast<GLsizei>(c.height); |
| GLint border = static_cast<GLint>(c.border); |
| GLsizei image_size = static_cast<GLsizei>(c.imageSize); |
| - const void* data = GetImmediateDataAs<const void*>(c); |
| - // Immediate version. |
| - // TODO(gman): Handle case where data is NULL. |
| + const void* data = GetImmediateDataAs<const void*>( |
| + c, image_size, immediate_data_size); |
| if (!data) { |
| return parse_error::kParseOutOfBounds; |
| } |
| @@ -1153,7 +1621,12 @@ |
| SetGLError(GL_INVALID_VALUE); |
| return parse_error::kParseNoError; |
| } |
| - // TODO(gman): Validate case where data is NULL. |
| + scoped_array<int8> zero; |
| + if (!pixels) { |
| + zero.reset(new int8[pixels_size]); |
| + memset(zero.get(), 0, pixels_size); |
| + pixels = zero.get(); |
| + } |
| glTexImage2D( |
| target, level, internal_format, width, height, border, format, type, |
| pixels); |
| @@ -1170,9 +1643,10 @@ |
| GLint border = static_cast<GLint>(c.border); |
| GLenum format = static_cast<GLenum>(c.format); |
| GLenum type = static_cast<GLenum>(c.type); |
| - const void* pixels = GetImmediateDataAs<const void*>(c); |
| - // Immediate version. |
| - // TODO(gman): Handle case where data is NULL. |
| + uint32 size = GLES2Util::ComputeImageDataSize( |
| + width, height, format, type, unpack_alignment_); |
| + const void* pixels = GetImmediateDataAs<const void*>( |
| + c, size, immediate_data_size); |
| if (!pixels) { |
| return parse_error::kParseOutOfBounds; |
| } |