1// Copyright (c) 2012 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// This file contains the implementation of the command parser.
6
7#include "gpu/command_buffer/service/cmd_parser.h"
8
9#include "base/logging.h"
10#include "base/debug/trace_event.h"
11
12namespace gpu {
13
14CommandParser::CommandParser(AsyncAPIInterface* handler)
15    : get_(0),
16      put_(0),
17      buffer_(NULL),
18      entry_count_(0),
19      handler_(handler) {
20}
21
22void CommandParser::SetBuffer(
23    void* shm_address,
24    size_t shm_size,
25    ptrdiff_t offset,
26    size_t size) {
27  // check proper alignments.
28  DCHECK_EQ(0, (reinterpret_cast<intptr_t>(shm_address)) % 4);
29  DCHECK_EQ(0, offset % 4);
30  DCHECK_EQ(0u, size % 4);
31  // check that the command buffer fits into the memory buffer.
32  DCHECK_GE(shm_size, offset + size);
33  get_ = 0;
34  put_ = 0;
35  char* buffer_begin = static_cast<char*>(shm_address) + offset;
36  buffer_ = reinterpret_cast<CommandBufferEntry*>(buffer_begin);
37  entry_count_ = size / 4;
38}
39
40// Process one command, reading the header from the command buffer, and
41// forwarding the command index and the arguments to the handler.
42// Note that:
43// - validation needs to happen on a copy of the data (to avoid race
44// conditions). This function only validates the header, leaving the arguments
45// validation to the handler, so it can pass a reference to them.
46// - get_ is modified *after* the command has been executed.
47error::Error CommandParser::ProcessCommand() {
48  CommandBufferOffset get = get_;
49  if (get == put_)
50    return error::kNoError;
51
52  CommandHeader header = buffer_[get].value_header;
53  if (header.size == 0) {
54    DVLOG(1) << "Error: zero sized command in command buffer";
55    return error::kInvalidSize;
56  }
57
58  if (static_cast<int>(header.size) + get > entry_count_) {
59    DVLOG(1) << "Error: get offset out of bounds";
60    return error::kOutOfBounds;
61  }
62
63  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cb_command"),
64               handler_->GetCommandName(header.command));
65
66  error::Error result = handler_->DoCommand(
67      header.command, header.size - 1, buffer_ + get);
68
69  // TODO(gman): If you want to log errors this is the best place to catch them.
70  //     It seems like we need an official way to turn on a debug mode and
71  //     get these errors.
72  if (error::IsError(result)) {
73    ReportError(header.command, result);
74  }
75
76  // If get was not set somewhere else advance it.
77  if (get == get_ && result != error::kDeferCommandUntilLater)
78    get_ = (get + header.size) % entry_count_;
79
80  return result;
81}
82
83void CommandParser::ReportError(unsigned int command_id,
84                                error::Error result) {
85  DVLOG(1) << "Error: " << result << " for Command "
86           << handler_->GetCommandName(command_id);
87}
88
89// Processes all the commands, while the buffer is not empty. Stop if an error
90// is encountered.
91error::Error CommandParser::ProcessAllCommands() {
92  while (!IsEmpty()) {
93    error::Error error = ProcessCommand();
94    if (error)
95      return error;
96  }
97  return error::kNoError;
98}
99
100}  // namespace gpu
101