Chromium Code Reviews| 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 d0b1c69ee505c718602fb41638fbaa866af50cb8..0d2417cac148defe20b9fa3312332139a595ed06 100644 |
| --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc |
| +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc |
| @@ -572,6 +572,11 @@ class GLES2DecoderImpl : public GLES2Decoder, |
| unsigned int arg_count, |
| const void* args) OVERRIDE; |
| + virtual error::Error DoCommands(unsigned int num_commands, |
| + const void* buffer, |
| + int num_entries, |
| + int* entries_processed) OVERRIDE; |
| + |
| // Overridden from AsyncAPIInterface. |
| virtual const char* GetCommandName(unsigned int command_id) const OVERRIDE; |
| @@ -1639,6 +1644,10 @@ class GLES2DecoderImpl : public GLES2Decoder, |
| return error::kNoError; |
| } |
| + // Set remaining commands to process to 0 to force DoCommands to return |
| + // and allow context preemption and GPU watchdog checks in GpuScheduler(). |
| + void ExitCommandProcessingEarly() { commands_to_process_ = 0; } |
| + |
| void ProcessPendingReadPixels(); |
| void FinishReadPixels(const cmds::ReadPixels& c, GLuint buffer); |
| @@ -1760,6 +1769,9 @@ class GLES2DecoderImpl : public GLES2Decoder, |
| int frame_number_; |
| + // Number of commands remaining to be processed in DoCommands(). |
| + int commands_to_process_; |
| + |
| bool has_robustness_extension_; |
| GLenum reset_status_; |
| bool reset_by_robustness_extension_; |
| @@ -3748,62 +3760,115 @@ const char* GLES2DecoderImpl::GetCommandName(unsigned int command_id) const { |
| return GetCommonCommandName(static_cast<cmd::CommandId>(command_id)); |
| } |
| -// Decode command with its arguments, and call the corresponding GL function. |
| -// Note: args is a pointer to the command buffer. As such, it could be changed |
| -// by a (malicious) client at any time, so if validation has to happen, it |
| -// should operate on a copy of them. |
| -error::Error GLES2DecoderImpl::DoCommand( |
| - unsigned int command, |
| - unsigned int arg_count, |
| - const void* cmd_data) { |
| +// Decode a command, and call the corresponding GL functions. |
| +// NOTE: DoCommand() is slower than calling DoCommands() on larger batches |
| +// of commands at once, and is now only used for tests that need to track |
| +// individual commands. |
| +error::Error GLES2DecoderImpl::DoCommand(unsigned int command, |
| + unsigned int arg_count, |
| + const void* cmd_data) { |
| + return DoCommands(1, cmd_data, arg_count + 1, 0); |
| +} |
| + |
| +// Decode multiple commands, and call the corresponding GL functions. |
| +// NOTE: 'buffer' is a pointer to the command buffer. As such, it could be |
| +// changed by a (malicious) client at any time, so if validation has to happen, |
| +// it should operate on a copy of them. |
| +// NOTE: This is duplicating code from AsyncAPIInterface::DoCommands() in the |
| +// interest of performance in this critical execution loop. |
| +error::Error GLES2DecoderImpl::DoCommands(unsigned int num_commands, |
| + const void* buffer, |
| + int num_entries, |
| + int* entries_processed) { |
| + commands_to_process_ = num_commands; |
| error::Error result = error::kNoError; |
| - if (log_commands()) { |
| - // TODO(notme): Change this to a LOG/VLOG that works in release. Tried |
| - // VLOG(1), no luck. |
| - LOG(ERROR) << "[" << logger_.GetLogPrefix() << "]" << "cmd: " |
| - << GetCommandName(command); |
| - } |
| - unsigned int command_index = command - kStartPoint - 1; |
| - if (command_index < arraysize(command_info)) { |
| - const CommandInfo& info = command_info[command_index]; |
| - unsigned int info_arg_count = static_cast<unsigned int>(info.arg_count); |
| - if ((info.arg_flags == cmd::kFixed && arg_count == info_arg_count) || |
| - (info.arg_flags == cmd::kAtLeastN && arg_count >= info_arg_count)) { |
| - bool doing_gpu_trace = false; |
| - if (gpu_trace_commands_) { |
| - if (CMD_FLAG_GET_TRACE_LEVEL(info.cmd_flags) <= gpu_trace_level_) { |
| - doing_gpu_trace = true; |
| - gpu_tracer_->Begin(GetCommandName(command), kTraceDecoder); |
| - } |
| + const CommandBufferEntry* cmd_data = |
| + static_cast<const CommandBufferEntry*>(buffer); |
| + int process_pos = 0; |
| + |
| + while (process_pos < num_entries && result == error::kNoError && |
| + commands_to_process_--) { |
| + CommandHeader header = cmd_data->value_header; |
| + if (header.size == 0) { |
|
piman
2014/09/09 00:59:01
nit: It was already before, but it's easier to see
vmiura
2014/09/09 01:38:56
Done.
|
| + DVLOG(1) << "Error: zero sized command in command buffer"; |
| + if (entries_processed) |
| + *entries_processed = process_pos; |
| + return error::kInvalidSize; |
| + } |
| + |
| + if (static_cast<int>(header.size) + process_pos > num_entries) { |
| + DVLOG(1) << "Error: get offset out of bounds"; |
| + if (entries_processed) |
| + *entries_processed = process_pos; |
| + return error::kOutOfBounds; |
| + } |
| + |
| + TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cb_command"), |
| + GetCommandName(header.command)); |
| + |
| + { |
|
piman
2014/09/09 00:59:01
nit: do you need this scope? I don't think you're
vmiura
2014/09/09 01:38:56
Done.
|
| + const unsigned int command = header.command; |
| + const unsigned int arg_count = header.size - 1; |
| + |
| + if (log_commands()) { |
| + // TODO(notme): Change this to a LOG/VLOG that works in release. Tried |
| + // VLOG(1), no luck. |
| + LOG(ERROR) << "[" << logger_.GetLogPrefix() << "]" |
| + << "cmd: " << GetCommandName(command); |
| } |
| + unsigned int command_index = command - kStartPoint - 1; |
| + if (command_index < arraysize(command_info)) { |
| + const CommandInfo& info = command_info[command_index]; |
| + unsigned int info_arg_count = static_cast<unsigned int>(info.arg_count); |
| + if ((info.arg_flags == cmd::kFixed && arg_count == info_arg_count) || |
| + (info.arg_flags == cmd::kAtLeastN && arg_count >= info_arg_count)) { |
| + bool doing_gpu_trace = false; |
| + if (gpu_trace_commands_) { |
| + if (CMD_FLAG_GET_TRACE_LEVEL(info.cmd_flags) <= gpu_trace_level_) { |
| + doing_gpu_trace = true; |
| + gpu_tracer_->Begin(GetCommandName(command), kTraceDecoder); |
| + } |
| + } |
| - uint32 immediate_data_size = |
| - (arg_count - info_arg_count) * sizeof(CommandBufferEntry); // NOLINT |
| + uint32 immediate_data_size = (arg_count - info_arg_count) * |
| + sizeof(CommandBufferEntry); // NOLINT |
| - result = (this->*info.cmd_handler)(immediate_data_size, cmd_data); |
| + result = (this->*info.cmd_handler)(immediate_data_size, cmd_data); |
| - if (doing_gpu_trace) |
| - gpu_tracer_->End(kTraceDecoder); |
| + if (doing_gpu_trace) |
| + gpu_tracer_->End(kTraceDecoder); |
| - if (debug()) { |
| - GLenum error; |
| - while ((error = glGetError()) != GL_NO_ERROR) { |
| - LOG(ERROR) << "[" << logger_.GetLogPrefix() << "] " |
| - << "GL ERROR: " << GLES2Util::GetStringEnum(error) << " : " |
| - << GetCommandName(command); |
| - LOCAL_SET_GL_ERROR(error, "DoCommand", "GL error from driver"); |
| + if (debug()) { |
| + GLenum error; |
| + while ((error = glGetError()) != GL_NO_ERROR) { |
| + LOG(ERROR) << "[" << logger_.GetLogPrefix() << "] " |
| + << "GL ERROR: " << GLES2Util::GetStringEnum(error) |
| + << " : " << GetCommandName(command); |
| + LOCAL_SET_GL_ERROR(error, "DoCommand", "GL error from driver"); |
| + } |
| + } |
| + } else { |
| + result = error::kInvalidArguments; |
| } |
| + } else { |
| + result = DoCommonCommand(command, arg_count, cmd_data); |
| + } |
| + if (result == error::kNoError && |
| + current_decoder_error_ != error::kNoError) { |
| + result = current_decoder_error_; |
| + current_decoder_error_ = error::kNoError; |
| } |
| - } else { |
| - result = error::kInvalidArguments; |
| } |
| - } else { |
| - result = DoCommonCommand(command, arg_count, cmd_data); |
| - } |
| - if (result == error::kNoError && current_decoder_error_ != error::kNoError) { |
| - result = current_decoder_error_; |
| - current_decoder_error_ = error::kNoError; |
| + |
| + if (result != error::kDeferCommandUntilLater) { |
| + process_pos += header.size; |
| + cmd_data += header.size; |
| + } |
| } |
| + |
| + if (entries_processed) |
| + *entries_processed = process_pos; |
| + |
| return result; |
| } |
| @@ -5698,6 +5763,10 @@ void GLES2DecoderImpl::DoLinkProgram(GLuint program_id) { |
| program_manager()->ClearUniforms(program); |
| } |
| } |
| + |
| + // LinkProgram can be very slow. Exit command processing to allow for |
| + // context preemption and GPU watchdog checks. |
| + ExitCommandProcessingEarly(); |
| }; |
| void GLES2DecoderImpl::DoTexParameterf( |
| @@ -6826,7 +6895,11 @@ void GLES2DecoderImpl::DoCompileShader(GLuint client_id) { |
| translator, |
| feature_info_->feature_flags().angle_translated_shader_source ? |
| ProgramManager::kANGLE : ProgramManager::kGL); |
| -}; |
| + |
| + // CompileShader can be very slow. Exit command processing to allow for |
| + // context preemption and GPU watchdog checks. |
| + ExitCommandProcessingEarly(); |
| +} |
| void GLES2DecoderImpl::DoGetShaderiv( |
| GLuint shader_id, GLenum pname, GLint* params) { |
| @@ -8325,6 +8398,10 @@ error::Error GLES2DecoderImpl::DoCompressedTexImage2D( |
| texture_ref, target, level, internal_format, |
| width, height, 1, border, 0, 0, true); |
| } |
| + |
| + // This may be a slow command. Exit command processing to allow for |
| + // context preemption and GPU watchdog checks. |
| + ExitCommandProcessingEarly(); |
| return error::kNoError; |
| } |
| @@ -8472,6 +8549,10 @@ error::Error GLES2DecoderImpl::HandleTexImage2D(uint32 immediate_data_size, |
| pixels, pixels_size}; |
| texture_manager()->ValidateAndDoTexImage2D( |
| &texture_state_, &state_, &framebuffer_state_, args); |
| + |
| + // This may be a slow command. Exit command processing to allow for |
| + // context preemption and GPU watchdog checks. |
| + ExitCommandProcessingEarly(); |
| return error::kNoError; |
| } |
| @@ -8530,6 +8611,10 @@ void GLES2DecoderImpl::DoCompressedTexSubImage2D( |
| // CompressedTexImage2D already cleared the texture. |
| glCompressedTexSubImage2D( |
| target, level, xoffset, yoffset, width, height, format, image_size, data); |
| + |
| + // This may be a slow command. Exit command processing to allow for |
| + // context preemption and GPU watchdog checks. |
| + ExitCommandProcessingEarly(); |
| } |
| static void Clip( |
| @@ -8675,6 +8760,10 @@ void GLES2DecoderImpl::DoCopyTexImage2D( |
| texture_ref, target, level, internal_format, width, height, 1, |
| border, internal_format, GL_UNSIGNED_BYTE, true); |
| } |
| + |
| + // This may be a slow command. Exit command processing to allow for |
| + // context preemption and GPU watchdog checks. |
| + ExitCommandProcessingEarly(); |
| } |
| void GLES2DecoderImpl::DoCopyTexSubImage2D( |
| @@ -8785,6 +8874,10 @@ void GLES2DecoderImpl::DoCopyTexSubImage2D( |
| destX, destY, copyX, copyY, |
| copyWidth, copyHeight); |
| } |
| + |
| + // This may be a slow command. Exit command processing to allow for |
| + // context preemption and GPU watchdog checks. |
| + ExitCommandProcessingEarly(); |
| } |
| bool GLES2DecoderImpl::ValidateTexSubImage2D( |
| @@ -8915,6 +9008,10 @@ error::Error GLES2DecoderImpl::DoTexSubImage2D( |
| target, level, xoffset, yoffset, width, height, format, type, data); |
| } |
| texture_manager()->SetLevelCleared(texture_ref, target, level, true); |
| + |
| + // This may be a slow command. Exit command processing to allow for |
| + // context preemption and GPU watchdog checks. |
| + ExitCommandProcessingEarly(); |
| return error::kNoError; |
| } |
| @@ -9401,6 +9498,10 @@ void GLES2DecoderImpl::DoSwapBuffers() { |
| LoseContext(GL_UNKNOWN_CONTEXT_RESET_ARB); |
| } |
| } |
| + |
| + // This may be a slow command. Exit command processing to allow for |
| + // context preemption and GPU watchdog checks. |
| + ExitCommandProcessingEarly(); |
| } |
| error::Error GLES2DecoderImpl::HandleEnableFeatureCHROMIUM( |